feat(napi-derive-backend, napi-derive): add support for string enums (#1551)
This commit is contained in:
parent
71e44be73d
commit
7c4dc2a2bd
9 changed files with 132 additions and 72 deletions
|
@ -1,4 +1,4 @@
|
|||
use proc_macro2::Ident;
|
||||
use proc_macro2::{Ident, Literal};
|
||||
use syn::{Attribute, Expr, Type};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -133,10 +133,25 @@ pub struct NapiEnum {
|
|||
pub skip_typescript: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum NapiEnumValue {
|
||||
String(String),
|
||||
Number(i32),
|
||||
}
|
||||
|
||||
impl Into<Literal> for &NapiEnumValue {
|
||||
fn into(self) -> Literal {
|
||||
match self {
|
||||
NapiEnumValue::String(string) => Literal::string(string),
|
||||
NapiEnumValue::Number(number) => Literal::i32_unsuffixed(number.to_owned()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NapiEnumVariant {
|
||||
pub name: Ident,
|
||||
pub val: i32,
|
||||
pub val: NapiEnumValue,
|
||||
pub comments: Vec<String>,
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ impl NapiEnum {
|
|||
let mut to_napi_branches = vec![];
|
||||
|
||||
self.variants.iter().for_each(|v| {
|
||||
let val = Literal::i32_unsuffixed(v.val);
|
||||
let val: Literal = (&v.val).into();
|
||||
let v_name = &v.name;
|
||||
|
||||
from_napi_branches.push(quote! { #val => Ok(#name::#v_name) });
|
||||
|
@ -62,7 +62,7 @@ impl NapiEnum {
|
|||
env: napi::bindgen_prelude::sys::napi_env,
|
||||
napi_val: napi::bindgen_prelude::sys::napi_value
|
||||
) -> napi::bindgen_prelude::Result<Self> {
|
||||
let val = i32::from_napi_value(env, napi_val).map_err(|e| {
|
||||
let val = FromNapiValue::from_napi_value(env, napi_val).map_err(|e| {
|
||||
napi::bindgen_prelude::error!(
|
||||
e.status,
|
||||
"Failed to convert napi value into enum `{}`. {}",
|
||||
|
@ -76,7 +76,7 @@ impl NapiEnum {
|
|||
_ => {
|
||||
Err(napi::bindgen_prelude::error!(
|
||||
napi::bindgen_prelude::Status::InvalidArg,
|
||||
"value `{}` does not match any variant of enum `{}`",
|
||||
"value `{:?}` does not match any variant of enum `{}`",
|
||||
val,
|
||||
#name_str
|
||||
))
|
||||
|
@ -94,7 +94,7 @@ impl NapiEnum {
|
|||
#(#to_napi_branches,)*
|
||||
};
|
||||
|
||||
i32::to_napi_value(env, val)
|
||||
ToNapiValue::to_napi_value(env, val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -109,13 +109,17 @@ impl NapiEnum {
|
|||
|
||||
for variant in self.variants.iter() {
|
||||
let name_lit = Literal::string(&format!("{}\0", variant.name));
|
||||
let val_lit = Literal::i32_unsuffixed(variant.val);
|
||||
let val_lit: Literal = (&variant.val).into();
|
||||
|
||||
define_properties.push(quote! {
|
||||
{
|
||||
let name = std::ffi::CStr::from_bytes_with_nul_unchecked(#name_lit.as_bytes());
|
||||
napi::bindgen_prelude::check_status!(
|
||||
napi::bindgen_prelude::sys::napi_set_named_property(env, obj_ptr, name.as_ptr(), i32::to_napi_value(env, #val_lit)?),
|
||||
napi::bindgen_prelude::sys::napi_set_named_property(
|
||||
env,
|
||||
obj_ptr, name.as_ptr(),
|
||||
ToNapiValue::to_napi_value(env, #val_lit)?
|
||||
),
|
||||
"Failed to defined enum `{}`",
|
||||
#js_name_lit
|
||||
)?;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use super::{add_alias, ToTypeDef, TypeDef};
|
||||
use crate::{js_doc_from_comments, NapiEnum};
|
||||
use crate::{js_doc_from_comments, NapiEnum, NapiEnumValue};
|
||||
|
||||
impl ToTypeDef for NapiEnum {
|
||||
fn to_type_def(&self) -> Option<TypeDef> {
|
||||
|
@ -26,12 +26,11 @@ impl NapiEnum {
|
|||
.variants
|
||||
.iter()
|
||||
.map(|v| {
|
||||
format!(
|
||||
"{}{} = {}",
|
||||
js_doc_from_comments(&v.comments),
|
||||
v.name,
|
||||
v.val,
|
||||
)
|
||||
let val = match &v.val {
|
||||
NapiEnumValue::Number(num) => format!("{}", num),
|
||||
NapiEnumValue::String(string) => format!("'{}'", string),
|
||||
};
|
||||
format!("{}{} = {}", js_doc_from_comments(&v.comments), v.name, val)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(",\n ")
|
||||
|
|
|
@ -38,7 +38,7 @@ pub struct BindgenAttrs {
|
|||
}
|
||||
|
||||
// NOTE: borrowed from wasm-bindgen
|
||||
// some of them may useless is #[napi] macro
|
||||
// some of them may useless in #[napi] macro
|
||||
macro_rules! attrgen {
|
||||
($mac:ident) => {
|
||||
$mac! {
|
||||
|
@ -65,6 +65,7 @@ macro_rules! attrgen {
|
|||
(ts_return_type, TsReturnType(Span, String, Span)),
|
||||
(ts_type, TsType(Span, String, Span)),
|
||||
(ts_generic_types, TsGenericTypes(Span, String, Span)),
|
||||
(string_enum, StringEnum(Span)),
|
||||
|
||||
// impl later
|
||||
// (inspectable, Inspectable(Span)),
|
||||
|
|
|
@ -9,7 +9,7 @@ use attrs::{BindgenAttr, BindgenAttrs};
|
|||
|
||||
use convert_case::{Case, Casing};
|
||||
use napi_derive_backend::{
|
||||
BindgenResult, CallbackArg, Diagnostic, FnKind, FnSelf, Napi, NapiConst, NapiEnum,
|
||||
BindgenResult, CallbackArg, Diagnostic, FnKind, FnSelf, Napi, NapiConst, NapiEnum, NapiEnumValue,
|
||||
NapiEnumVariant, NapiFn, NapiFnArg, NapiFnArgKind, NapiImpl, NapiItem, NapiStruct,
|
||||
NapiStructField, NapiStructKind,
|
||||
};
|
||||
|
@ -1124,15 +1124,37 @@ impl ConvertToAST for syn::ItemEnum {
|
|||
.js_name()
|
||||
.map_or_else(|| self.ident.to_string(), |(s, _)| s.to_string());
|
||||
|
||||
let mut last_variant_val: i32 = -1;
|
||||
let variants = self
|
||||
let variants = match opts.string_enum() {
|
||||
Some(_) => self
|
||||
.variants
|
||||
.iter()
|
||||
.map(|v| {
|
||||
match v.fields {
|
||||
syn::Fields::Unit => {}
|
||||
_ => bail_span!(v.fields, "Structured enum is not supported in #[napi]"),
|
||||
};
|
||||
if !matches!(v.fields, syn::Fields::Unit) {
|
||||
bail_span!(v.fields, "Structured enum is not supported in #[napi]")
|
||||
}
|
||||
if matches!(&v.discriminant, Some((_, _))) {
|
||||
bail_span!(
|
||||
v.fields,
|
||||
"Literal values are not supported with string enum in #[napi]"
|
||||
)
|
||||
}
|
||||
Ok(NapiEnumVariant {
|
||||
name: v.ident.clone(),
|
||||
val: NapiEnumValue::String(v.ident.to_string()),
|
||||
comments: extract_doc_comments(&v.attrs),
|
||||
})
|
||||
})
|
||||
.collect::<BindgenResult<Vec<NapiEnumVariant>>>()?,
|
||||
None => {
|
||||
let mut last_variant_val: i32 = -1;
|
||||
|
||||
self
|
||||
.variants
|
||||
.iter()
|
||||
.map(|v| {
|
||||
if !matches!(v.fields, syn::Fields::Unit) {
|
||||
bail_span!(v.fields, "Structured enum is not supported in #[napi]")
|
||||
}
|
||||
|
||||
let val = match &v.discriminant {
|
||||
Some((_, expr)) => {
|
||||
|
@ -1176,11 +1198,13 @@ impl ConvertToAST for syn::ItemEnum {
|
|||
|
||||
Ok(NapiEnumVariant {
|
||||
name: v.ident.clone(),
|
||||
val,
|
||||
val: NapiEnumValue::Number(val),
|
||||
comments: extract_doc_comments(&v.attrs),
|
||||
})
|
||||
})
|
||||
.collect::<BindgenResult<Vec<NapiEnumVariant>>>()?;
|
||||
.collect::<BindgenResult<Vec<NapiEnumVariant>>>()?
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Napi {
|
||||
item: NapiItem::Enum(NapiEnum {
|
||||
|
|
|
@ -98,6 +98,11 @@ Generated by [AVA](https://avajs.dev).
|
|||
export const enum Empty {␊
|
||||
␊
|
||||
}␊
|
||||
export const enum Status {␊
|
||||
Pristine = 'Pristine',␊
|
||||
Loading = 'Loading',␊
|
||||
Ready = 'Ready'␊
|
||||
}␊
|
||||
/** You could break the step and for an new continuous value. */␊
|
||||
export const enum CustomNumEnum {␊
|
||||
One = 1,␊
|
||||
|
|
Binary file not shown.
5
examples/napi/index.d.ts
vendored
5
examples/napi/index.d.ts
vendored
|
@ -87,6 +87,11 @@ export const enum Kind {
|
|||
}
|
||||
export const enum Empty {
|
||||
|
||||
}
|
||||
export const enum Status {
|
||||
Pristine = 'Pristine',
|
||||
Loading = 'Loading',
|
||||
Ready = 'Ready'
|
||||
}
|
||||
/** You could break the step and for an new continuous value. */
|
||||
export const enum CustomNumEnum {
|
||||
|
|
|
@ -14,6 +14,13 @@ pub enum Kind {
|
|||
#[napi]
|
||||
pub enum Empty {}
|
||||
|
||||
#[napi(string_enum)]
|
||||
pub enum Status {
|
||||
Pristine,
|
||||
Loading,
|
||||
Ready,
|
||||
}
|
||||
|
||||
/// You could break the step and for an new continuous value.
|
||||
#[napi]
|
||||
pub enum CustomNumEnum {
|
||||
|
|
Loading…
Reference in a new issue