Repository: napi-rs/napi-rs
File: crates/backend/src/codegen/struct.rs (Rust)

Code before:
      #[napi::ctor::ctor(crate_path=napi::ctor)]
      fn #struct_register_name() {
        napi::__private::register_class(std::any::TypeId::of::<#name>(), #js_mod_ident, #js_name, vec![#(#props),*]);
      }

      #[allow(non_snake_case)]
      #[allow(clippy::all)]
      #[cfg(all(not(test), target_family = "wasm"))]
      #[no_mangle]
      extern "C" fn #struct_register_name() {
        napi::__private::register_class(std::any::TypeId::of::<#name>(), #js_mod_ident, #js_name, vec![#(#props),*]);
      }
    }
  }

  fn gen_to_napi_value_structured_enum_impl(
    &self,
    structured_enum: &NapiStructuredEnum,
  ) -> TokenStream {
    let name = &self.name;
    let name_str = self.name.to_string();
    let mut discriminant = structured_enum.discriminant.clone();
    if let Some(case) = structured_enum.discriminant_case {
      discriminant = discriminant.to_case(case);
    }
    let discriminant = discriminant.as_str();

    let mut variant_arm_setters = vec![];
    let mut variant_arm_getters = vec![];

    for variant in structured_enum.variants.iter() {
      let variant_name = &variant.name;
      let variant_name_str = variant_name.to_string();
      let mut obj_field_setters = vec![quote! {
        obj.set(#discriminant, #variant_name_str)?;
      }];
      let mut obj_field_getters = vec![];
      let mut field_destructions = vec![];
      for field in variant.fields.iter() {
        let field_js_name = &field.js_name;
        let mut ty = field.ty.clone();
        remove_lifetime_in_type(&mut ty);
        let is_optional_field = if let syn::Type::Path(syn::TypePath {
          path: syn::Path { segments, .. },
          ..
        }) = &ty
        {
          if let Some(last_path) = segments.last() {
            last_path.ident == "Option"
          } else {
            false

Review comment (suggestion):
discriminant_case should apply to the discriminant values (variant tags), not the discriminant key. This code transforms the key name instead. Keep the key unchanged (use structured_enum.discriminant as-is) and apply case conversion when emitting/reading the tag value for each variant, e.g., by converting variant.name via to_case(case) at the point where tag strings are produced/compared.

Code after:
      #[napi::ctor::ctor(crate_path=napi::ctor)]
      fn #struct_register_name() {
        napi::__private::register_class(std::any::TypeId::of::<#name>(), #js_mod_ident, #js_name, vec![#(#props),*]);
      }

      #[allow(non_snake_case)]
      #[allow(clippy::all)]
      #[cfg(all(not(test), target_family = "wasm"))]
      #[no_mangle]
      extern "C" fn #struct_register_name() {
        napi::__private::register_class(std::any::TypeId::of::<#name>(), #js_mod_ident, #js_name, vec![#(#props),*]);
      }
    }
  }

  fn gen_to_napi_value_structured_enum_impl(
    &self,
    structured_enum: &NapiStructuredEnum,
  ) -> TokenStream {
    let name = &self.name;
    let name_str = self.name.to_string();
    let discriminant = structured_enum.discriminant.as_str();

    let mut variant_arm_setters = vec![];
    let mut variant_arm_getters = vec![];

    for variant in structured_enum.variants.iter() {
      let variant_name = &variant.name;
      let mut variant_name_str = variant_name.to_string();
      if let Some(case) = structured_enum.discriminant_case {
        variant_name_str = variant_name_str.to_case(case);
      }
      let mut obj_field_setters = vec![quote! {
        obj.set(#discriminant, #variant_name_str)?;
      }];
      let mut obj_field_getters = vec![];
      let mut field_destructions = vec![];
      for field in variant.fields.iter() {
        let field_js_name = &field.js_name;
        let mut ty = field.ty.clone();
        remove_lifetime_in_type(&mut ty);
        let is_optional_field = if let syn::Type::Path(syn::TypePath {
          path: syn::Path { segments, .. },
          ..
        }) = &ty
        {
          if let Some(last_path) = segments.last() {
            last_path.ident == "Option"
          } else {
            false
          }