feat(napi-derive): add optional enum_string case conversion (#1995)

This commit is contained in:
Andrew Toth 2024-03-11 08:10:00 -04:00 committed by GitHub
parent d962e34d3a
commit 6b1058a268
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 91 additions and 19 deletions

View file

@ -71,7 +71,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)),
(string_enum, StringEnum(Span, Option<(String, Span)>)),
(use_nullable, UseNullable(Span, Option<bool>), false),
// impl later
@ -132,6 +132,21 @@ macro_rules! methods {
}
};
(@method $name:ident, $variant:ident(Span, Option<(String, Span)>)) => {
pub fn $name(&self) -> Option<Option<&(String, Span)>> {
self.attrs
.iter()
.filter_map(|a| match &a.1 {
BindgenAttr::$variant(_, s) => {
a.0.set(true);
Some(s.as_ref())
}
_ => None,
})
.next()
}
};
(@method $name:ident, $variant:ident(Span, Option<bool>), $default_value:literal) => {
pub fn $name(&self) -> bool {
self.attrs
@ -396,6 +411,21 @@ impl Parse for BindgenAttr {
return Ok(BindgenAttr::$variant(attr_span, val, span))
});
(@parser $variant:ident(Span, Option<(String, Span)>)) => ({
if let Ok(_) = input.parse::<Token![=]>() {
let val = match input.parse::<syn::LitStr>() {
Ok(str) => Some((str.value(), str.span())),
Err(_) => {
let ident = input.parse::<AnyIdent>()?.0;
Some((ident.to_string(), ident.span()))
}
};
return Ok(BindgenAttr::$variant(attr_span, val))
} else {
return Ok(BindgenAttr::$variant(attr_span, None))
}
});
(@parser $variant:ident(Span, Option<bool>), $default_value:literal) => ({
if let Ok(_) = input.parse::<Token![=]>() {
let (val, _) = match input.parse::<syn::LitBool>() {

View file

@ -1062,26 +1062,48 @@ impl ConvertToAST for syn::ItemEnum {
.map_or_else(|| self.ident.to_string(), |(s, _)| s.to_string());
let variants = match opts.string_enum() {
Some(_) => self
.variants
.iter()
.map(|v| {
if !matches!(v.fields, syn::Fields::Unit) {
bail_span!(v.fields, "Structured enum is not supported in #[napi]")
Some(case) => {
let case = case.map(|c| Ok::<Case, Diagnostic>(match c.0.as_str() {
"lowercase" => Case::Flat,
"UPPERCASE" => Case::UpperFlat,
"PascalCase" => Case::Pascal,
"camelCase" => Case::Camel,
"snake_case" => Case::Snake,
"SCREAMING_SNAKE_CASE" => Case::UpperSnake,
"kebab-case" => Case::Kebab,
"SCREAMING-KEBAB-CASE" => Case::UpperKebab,
_ => {
bail_span!(self, "Unknown string enum case. Possible values are \"lowercase\", \"UPPERCASE\", \"PascalCase\", \"camelCase\", \"snake_case\", \"SCREAMING_SNAKE_CASE\", \"kebab-case\", or \"SCREAMING-KEBAB-CASE\"")
}
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),
})).transpose()?;
self
.variants
.iter()
.map(|v| {
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]"
)
}
let mut val = v.ident.to_string();
if let Some(case) = case {
val = val.to_case(case)
};
Ok(NapiEnumVariant {
name: v.ident.clone(),
val: NapiEnumValue::String(val),
comments: extract_doc_comments(&v.attrs),
})
})
})
.collect::<BindgenResult<Vec<NapiEnumVariant>>>()?,
.collect::<BindgenResult<Vec<NapiEnumVariant>>>()?
}
None => {
let mut last_variant_val: i32 = -1;

View file

@ -617,6 +617,12 @@ Generated by [AVA](https://avajs.dev).
name: string␊
}␊
export const enum StringEnum {␊
VariantOne = 'variantone',␊
VariantTwo = 'varianttwo',␊
VariantThree = 'variantthree'␊
}␊
export function sumBtreeMapping(nums: Record<string, number>): number␊
export function sumIndexMapping(nums: Record<string, number>): number␊

View file

@ -509,6 +509,7 @@ module.exports.roundtripStr = nativeBinding.roundtripStr
module.exports.runScript = nativeBinding.runScript
module.exports.setSymbolInObj = nativeBinding.setSymbolInObj
module.exports.Status = nativeBinding.Status
module.exports.StringEnum = nativeBinding.StringEnum
module.exports.sumBtreeMapping = nativeBinding.sumBtreeMapping
module.exports.sumIndexMapping = nativeBinding.sumIndexMapping
module.exports.sumMapping = nativeBinding.sumMapping

View file

@ -607,6 +607,12 @@ export interface StrictObject {
name: string
}
export const enum StringEnum {
VariantOne = 'variantone',
VariantTwo = 'varianttwo',
VariantThree = 'variantthree'
}
export function sumBtreeMapping(nums: Record<string, number>): number
export function sumIndexMapping(nums: Record<string, number>): number

View file

@ -19,6 +19,13 @@ pub enum Status {
Ready,
}
#[napi(string_enum = "lowercase")]
pub enum StringEnum {
VariantOne,
VariantTwo,
VariantThree,
}
/// You could break the step and for an new continuous value.
#[napi]
pub enum CustomNumEnum {