impl To/FromNapiValue for HashMap
This commit is contained in:
parent
e77d2e95ef
commit
f4c0b0639b
16 changed files with 170 additions and 21 deletions
|
@ -255,20 +255,25 @@ async function processIntermediateTypeFile(source: string, target: string) {
|
|||
}
|
||||
})
|
||||
|
||||
for (const [name, def] of impls.entries()) {
|
||||
const classDef = classes.get(name)
|
||||
for (const [name, classDef] of classes.entries()) {
|
||||
const implDef = impls.get(name)
|
||||
|
||||
dts += `export class ${name} {
|
||||
${(classDef ?? '')
|
||||
${classDef
|
||||
.split('\n')
|
||||
.map((line) => line.trim())
|
||||
.join('\n ')}
|
||||
${def
|
||||
.split('\n')
|
||||
.map((line) => line.trim())
|
||||
.join('\n ')}
|
||||
}
|
||||
`
|
||||
.join('\n ')}`
|
||||
|
||||
if (implDef) {
|
||||
dts +=
|
||||
'\n ' +
|
||||
implDef
|
||||
.split('\n')
|
||||
.map((line) => line.trim())
|
||||
.join('\n ')
|
||||
}
|
||||
|
||||
dts += '\n}\n'
|
||||
}
|
||||
|
||||
await unlinkAsync(source)
|
||||
|
|
|
@ -35,6 +35,7 @@ pub static TYPE_REGEXES: Lazy<HashMap<&'static str, Regex>> = Lazy::new(|| {
|
|||
("Vec", Regex::new(r"^Vec < (.*) >$").unwrap()),
|
||||
("Option", Regex::new(r"^Option < (.*) >").unwrap()),
|
||||
("Result", Regex::new(r"^Result < (.*) >").unwrap()),
|
||||
("HashMap", Regex::new(r"HashMap < (.*), (.*) >").unwrap()),
|
||||
]);
|
||||
|
||||
map
|
||||
|
@ -88,12 +89,24 @@ pub fn str_to_ts_type(ty: &str, omit_top_level_result: bool) -> String {
|
|||
s if s.starts_with("Result") && TYPE_REGEXES["Result"].is_match(s) => {
|
||||
let captures = TYPE_REGEXES["Result"].captures(s).unwrap();
|
||||
let inner = captures.get(1).unwrap().as_str();
|
||||
|
||||
if omit_top_level_result {
|
||||
str_to_ts_type(inner, false)
|
||||
} else {
|
||||
format!("Error | {}", str_to_ts_type(inner, false))
|
||||
}
|
||||
}
|
||||
s if TYPE_REGEXES["HashMap"].is_match(s) => {
|
||||
let captures = TYPE_REGEXES["HashMap"].captures(s).unwrap();
|
||||
let key = captures.get(1).unwrap().as_str();
|
||||
let val = captures.get(2).unwrap().as_str();
|
||||
|
||||
format!(
|
||||
"Record<{}, {}>",
|
||||
str_to_ts_type(key, false),
|
||||
str_to_ts_type(val, false)
|
||||
)
|
||||
}
|
||||
s => s.to_owned(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -685,9 +685,10 @@ impl ConvertToAST for syn::ItemStruct {
|
|||
|
||||
let (js_name, name) = match &field.ident {
|
||||
Some(ident) => (
|
||||
field_opts
|
||||
.js_name()
|
||||
.map_or_else(|| ident.to_string(), |(js_name, _)| js_name.to_owned()),
|
||||
field_opts.js_name().map_or_else(
|
||||
|| ident.to_string().to_case(Case::Camel),
|
||||
|(js_name, _)| js_name.to_owned(),
|
||||
),
|
||||
syn::Member::Named(ident.clone()),
|
||||
),
|
||||
None => {
|
||||
|
|
|
@ -4,6 +4,7 @@ use std::ptr;
|
|||
mod array;
|
||||
mod boolean;
|
||||
mod buffer;
|
||||
mod map;
|
||||
mod nil;
|
||||
mod number;
|
||||
mod object;
|
||||
|
|
44
crates/napi/src/bindgen_runtime/js_values/map.rs
Normal file
44
crates/napi/src/bindgen_runtime/js_values/map.rs
Normal file
|
@ -0,0 +1,44 @@
|
|||
use std::collections::HashMap;
|
||||
use std::hash::Hash;
|
||||
|
||||
use crate::bindgen_prelude::{Env, Result, ToNapiValue, *};
|
||||
|
||||
impl<K, V> TypeName for HashMap<K, V> {
|
||||
fn type_name() -> &'static str {
|
||||
"HashMap"
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> ToNapiValue for HashMap<K, V>
|
||||
where
|
||||
K: AsRef<str>,
|
||||
V: ToNapiValue,
|
||||
{
|
||||
unsafe fn to_napi_value(raw_env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
|
||||
let env = Env::from(raw_env);
|
||||
let mut obj = env.create_object()?;
|
||||
for (k, v) in val.into_iter() {
|
||||
obj.set(k.as_ref(), v)?;
|
||||
}
|
||||
|
||||
Object::to_napi_value(raw_env, obj)
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> FromNapiValue for HashMap<K, V>
|
||||
where
|
||||
K: From<String> + Eq + Hash,
|
||||
V: FromNapiValue,
|
||||
{
|
||||
unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
|
||||
let obj = Object::from_napi_value(env, napi_val)?;
|
||||
let mut map = HashMap::new();
|
||||
for key in Object::keys(&obj)?.into_iter() {
|
||||
if let Some(val) = obj.get(&key)? {
|
||||
map.insert(K::from(key), val);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(map)
|
||||
}
|
||||
}
|
|
@ -19,8 +19,8 @@ impl Object {
|
|||
Ok(Object { env, inner: ptr })
|
||||
}
|
||||
|
||||
pub fn get<T: FromNapiValue>(&self, field: String) -> Result<Option<T>> {
|
||||
let c_field = CString::new(field)?;
|
||||
pub fn get<K: AsRef<str>, V: FromNapiValue>(&self, field: K) -> Result<Option<V>> {
|
||||
let c_field = CString::new(field.as_ref())?;
|
||||
|
||||
unsafe {
|
||||
let mut ret = ptr::null_mut();
|
||||
|
@ -36,16 +36,16 @@ impl Object {
|
|||
Ok(if ty == ValueType::Undefined {
|
||||
None
|
||||
} else {
|
||||
Some(T::from_napi_value(self.env, ret)?)
|
||||
Some(V::from_napi_value(self.env, ret)?)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set<T: ToNapiValue>(&mut self, field: String, val: T) -> Result<()> {
|
||||
let c_field = CString::new(field)?;
|
||||
pub fn set<K: AsRef<str>, V: ToNapiValue>(&mut self, field: K, val: V) -> Result<()> {
|
||||
let c_field = CString::new(field.as_ref())?;
|
||||
|
||||
unsafe {
|
||||
let napi_val = T::to_napi_value(self.env, val)?;
|
||||
let napi_val = V::to_napi_value(self.env, val)?;
|
||||
|
||||
check_status!(
|
||||
sys::napi_set_named_property(self.env, self.inner, c_field.as_ptr(), napi_val),
|
||||
|
|
|
@ -13,7 +13,10 @@ napi3 = ["napi/napi3"]
|
|||
|
||||
[dependencies]
|
||||
napi-derive = { path = "../../crates/macro", features = ["type-def"] }
|
||||
napi = { path = "../../crates/napi", features = ["latin1"] }
|
||||
napi = { path = "../../crates/napi", features = ["latin1", "serde-json"] }
|
||||
serde = "1"
|
||||
serde_derive = "1"
|
||||
serde_json = "1"
|
||||
|
||||
[build-dependencies]
|
||||
napi-build = { path = "../../crates/build" }
|
|
@ -13,6 +13,7 @@ Generated by [AVA](https://avajs.dev).
|
|||
export function sumNums(nums: Array<number>): number␊
|
||||
export function getCwd(callback: (arg0: string) => void): void␊
|
||||
export function readFile(callback: (arg0: Error | undefined, arg1: string | null) => void): void␊
|
||||
export function readPackageJson(): PackageJson␊
|
||||
export enum Kind { Dog = 0, Cat = 1, Duck = 2 }␊
|
||||
export enum CustomNumEnum { One = 1, Two = 2, Three = 3, Four = 4, Six = 6, Eight = 8, Nine = 9, Ten = 10 }␊
|
||||
export function enumToI32(e: CustomNumEnum): number␊
|
||||
|
@ -33,4 +34,11 @@ Generated by [AVA](https://avajs.dev).
|
|||
set name(name: string)␊
|
||||
whoami(): string␊
|
||||
}␊
|
||||
export class PackageJson {␊
|
||||
name: string␊
|
||||
version: string␊
|
||||
dependencies: Record<string, string> | null␊
|
||||
devDependencies: Record<string, string> | null␊
|
||||
constructor(name: string, version: string, dependencies: Record<string, string> | null, devDependencies: Record<string, string> | null)␊
|
||||
}␊
|
||||
`
|
||||
|
|
Binary file not shown.
|
@ -20,6 +20,7 @@ import {
|
|||
mapOption,
|
||||
readFile,
|
||||
throwError,
|
||||
readPackageJson,
|
||||
} from '../'
|
||||
|
||||
test('number', (t) => {
|
||||
|
@ -67,6 +68,12 @@ test('class', (t) => {
|
|||
|
||||
dog.name = '可乐'
|
||||
t.is(dog.name, '可乐')
|
||||
|
||||
const packageJson = readPackageJson()
|
||||
t.is(packageJson.name, 'napi-rs')
|
||||
t.is(packageJson.version, '0.0.0')
|
||||
t.is(packageJson.dependencies, null)
|
||||
t.snapshot(Object.keys(packageJson.devDependencies!).sort())
|
||||
})
|
||||
|
||||
test('callback', (t) => {
|
||||
|
|
38
examples/napi/__test__/values.spec.ts.md
Normal file
38
examples/napi/__test__/values.spec.ts.md
Normal file
|
@ -0,0 +1,38 @@
|
|||
# Snapshot report for `examples/napi/__test__/values.spec.ts`
|
||||
|
||||
The actual snapshot is saved in `values.spec.ts.snap`.
|
||||
|
||||
Generated by [AVA](https://avajs.dev).
|
||||
|
||||
## class
|
||||
|
||||
> Snapshot 1
|
||||
|
||||
[
|
||||
'@types/debug',
|
||||
'@types/lodash-es',
|
||||
'@types/node',
|
||||
'@types/sinon',
|
||||
'@typescript-eslint/eslint-plugin',
|
||||
'@typescript-eslint/parser',
|
||||
'ava',
|
||||
'benny',
|
||||
'c8',
|
||||
'cross-env',
|
||||
'esbuild',
|
||||
'eslint',
|
||||
'eslint-config-prettier',
|
||||
'eslint-plugin-import',
|
||||
'eslint-plugin-prettier',
|
||||
'husky',
|
||||
'lerna',
|
||||
'lint-staged',
|
||||
'npm-run-all',
|
||||
'prettier',
|
||||
'shx',
|
||||
'sinon',
|
||||
'source-map-support',
|
||||
'ts-node',
|
||||
'tslib',
|
||||
'typescript',
|
||||
]
|
BIN
examples/napi/__test__/values.spec.ts.snap
Normal file
BIN
examples/napi/__test__/values.spec.ts.snap
Normal file
Binary file not shown.
8
examples/napi/index.d.ts
vendored
8
examples/napi/index.d.ts
vendored
|
@ -3,6 +3,7 @@ export function getNums(): Array<number>
|
|||
export function sumNums(nums: Array<number>): number
|
||||
export function getCwd(callback: (arg0: string) => void): void
|
||||
export function readFile(callback: (arg0: Error | undefined, arg1: string | null) => void): void
|
||||
export function readPackageJson(): PackageJson
|
||||
export enum Kind { Dog = 0, Cat = 1, Duck = 2 }
|
||||
export enum CustomNumEnum { One = 1, Two = 2, Three = 3, Four = 4, Six = 6, Eight = 8, Nine = 9, Ten = 10 }
|
||||
export function enumToI32(e: CustomNumEnum): number
|
||||
|
@ -23,3 +24,10 @@ export class Animal {
|
|||
set name(name: string)
|
||||
whoami(): string
|
||||
}
|
||||
export class PackageJson {
|
||||
name: string
|
||||
version: string
|
||||
dependencies: Record<string, string> | null
|
||||
devDependencies: Record<string, string> | null
|
||||
constructor(name: string, version: string, dependencies: Record<string, string> | null, devDependencies: Record<string, string> | null)
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use std::{collections::HashMap, fs};
|
||||
|
||||
use napi::bindgen_prelude::*;
|
||||
|
||||
use crate::r#enum::Kind;
|
||||
|
@ -40,3 +42,20 @@ impl Animal {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[napi(constructor)]
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
struct PackageJson {
|
||||
pub name: String,
|
||||
pub version: String,
|
||||
pub dependencies: Option<HashMap<String, String>>,
|
||||
#[serde(rename = "devDependencies")]
|
||||
pub dev_dependencies: Option<HashMap<String, String>>,
|
||||
}
|
||||
|
||||
#[napi]
|
||||
fn read_package_json() -> Result<PackageJson> {
|
||||
let raw = fs::read_to_string("package.json")?;
|
||||
let p: PackageJson = serde_json::from_str(&raw)?;
|
||||
Ok(p)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#[macro_use]
|
||||
extern crate napi_derive;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
mod array;
|
||||
mod callback;
|
||||
|
|
|
@ -8,7 +8,7 @@ fn list_obj_keys(obj: Object) -> Vec<String> {
|
|||
#[napi]
|
||||
fn create_obj(env: Env) -> Object {
|
||||
let mut obj = env.create_object().unwrap();
|
||||
obj.set("test".to_owned(), 1).unwrap();
|
||||
obj.set("test", 1).unwrap();
|
||||
|
||||
obj
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue