diff --git a/README.md b/README.md index 08c21cb6..9f321b6e 100644 --- a/README.md +++ b/README.md @@ -721,6 +721,33 @@ struct TestTypeA { ``` + + + +Renaming fields + + +```asn +Test-type-a ::= SEQUENCE { + notQuiteRustCase INTEGER +} +``` + + + + +```rust +use rasn::prelude::*; + +#[derive(AsnType, Decode, Encode)] +#[rasn(automatic_tags, identifier = "Test-type-a")] +struct TestTypeA { + #[rasn(identifier = "notQuiteRustCase")] + rust_case_indeed: Integer +} + +``` + diff --git a/macros/src/asn_type.rs b/macros/src/asn_type.rs index dd71f516..e26deafd 100644 --- a/macros/src/asn_type.rs +++ b/macros/src/asn_type.rs @@ -94,6 +94,11 @@ pub fn derive_struct_impl( let constraints_def = config.constraints.const_static_def(crate_root); + let alt_identifier = config.identifier.as_ref().map_or( + quote!(), + |id| quote!(const IDENTIFIER: Option<&'static str> = Some(#id);), + ); + quote! { #constructed_impl @@ -104,7 +109,7 @@ pub fn derive_struct_impl( #tag }; - + #alt_identifier #constraints_def } } diff --git a/macros/src/config.rs b/macros/src/config.rs index a17d3c57..3a257234 100644 --- a/macros/src/config.rs +++ b/macros/src/config.rs @@ -1,7 +1,7 @@ use std::ops::Deref; use quote::ToTokens; -use syn::{Lit, NestedMeta, Path, UnOp}; +use syn::{Lit, LitStr, NestedMeta, Path, UnOp}; use crate::{ext::TypeExt, tag::Tag}; @@ -214,6 +214,7 @@ impl Constraints { #[derive(Clone, Debug)] pub struct Config { pub crate_root: Path, + pub identifier: Option, pub enumerated: bool, pub choice: bool, pub set: bool, @@ -229,6 +230,7 @@ impl Config { let mut choice = false; let mut set = false; let mut crate_root = None; + let mut identifier = None; let mut enumerated = false; let mut automatic_tags = false; let mut tag = None; @@ -262,6 +264,13 @@ impl Config { _ => None, }; } + } else if path.is_ident("identifier") { + if let syn::Meta::NameValue(nv) = item { + identifier = match &nv.lit { + syn::Lit::Str(s) => Some(s.clone()), + _ => None, + }; + } } else if path.is_ident("enumerated") { enumerated = true; } else if path.is_ident("choice") { @@ -362,6 +371,7 @@ impl Config { option_type, set, tag, + identifier, constraints: Constraints { extensible, from, @@ -457,6 +467,7 @@ pub struct VariantConfig<'config> { container_config: &'config Config, generics: &'config syn::Generics, pub tag: Option, + pub identifier: Option, pub extension_addition: bool, pub constraints: Constraints, } @@ -468,6 +479,7 @@ impl<'config> VariantConfig<'config> { container_config: &'config Config, ) -> Self { let mut extensible = false; + let mut identifier = None; let mut extension_addition = false; let mut from = None; let mut size = None; @@ -488,6 +500,13 @@ impl<'config> VariantConfig<'config> { let path = item.path(); if path.is_ident("tag") { tag = Tag::from_meta(item); + } else if path.is_ident("identifier") { + if let syn::Meta::NameValue(nv) = item { + identifier = match &nv.lit { + syn::Lit::Str(s) => Some(s.clone()), + _ => None, + }; + } } else if path.is_ident("size") { size = Some(Value::from_meta(item)); } else if path.is_ident("value") { @@ -507,6 +526,7 @@ impl<'config> VariantConfig<'config> { extension_addition, generics, tag, + identifier, variant, constraints: Constraints { extensible, @@ -709,6 +729,7 @@ pub struct FieldConfig<'a> { pub field: &'a syn::Field, pub container_config: &'a Config, pub tag: Option, + pub identifier: Option, pub default: Option>, pub extension_addition: bool, pub extension_addition_group: bool, @@ -726,6 +747,7 @@ impl<'a> FieldConfig<'a> { let mut default = None; let mut tag = None; let mut size = None; + let mut identifier = None; let mut from = None; let mut value = None; let mut extensible = false; @@ -753,6 +775,13 @@ impl<'a> FieldConfig<'a> { }, _ => None, }); + } else if path.is_ident("identifier") { + if let syn::Meta::NameValue(nv) = item { + identifier = match &nv.lit { + syn::Lit::Str(s) => Some(s.clone()), + _ => None, + }; + } } else if path.is_ident("size") { size = Some(Value::from_meta(item)); } else if path.is_ident("value") { @@ -782,6 +811,7 @@ impl<'a> FieldConfig<'a> { container_config, default, field, + identifier, tag, extension_addition, extension_addition_group, @@ -1122,12 +1152,14 @@ impl<'a> FieldConfig<'a> { let tag = self.tag(context); let tag_tree = self.tag_tree(context); let name = self - .field - .ident - .as_ref() - .map_or(syn::LitStr::new("", proc_macro2::Span::call_site()), |id| { - syn::LitStr::new(&id.to_string(), proc_macro2::Span::call_site()) - }); + .identifier + .clone() + .or(self + .field + .ident + .as_ref() + .map(|id| syn::LitStr::new(&id.to_string(), proc_macro2::Span::call_site()))) + .unwrap_or(syn::LitStr::new("", proc_macro2::Span::call_site())); let constructor = quote::format_ident!( "{}", diff --git a/macros/src/enum.rs b/macros/src/enum.rs index 1829f909..229bddc6 100644 --- a/macros/src/enum.rs +++ b/macros/src/enum.rs @@ -75,7 +75,14 @@ impl Enum { let identifiers = self .variants .iter() - .map(|v| syn::LitStr::new(&v.ident.to_string(), proc_macro2::Span::call_site())) + .map(|v| { + VariantConfig::new(v, &self.generics, &self.config) + .identifier + .unwrap_or(syn::LitStr::new( + &v.ident.to_string(), + proc_macro2::Span::call_site(), + )) + }) .collect_vec(); let constraints_def = self.config.constraints.const_static_def(crate_root); @@ -128,10 +135,19 @@ impl Enum { const DISCRIMINANTS: &'static [(Self, isize)] = &[#(#discriminants,)*]; const EXTENDED_DISCRIMINANTS: Option<&'static [(Self, isize)]> = #extended_discriminants; + + const IDENTIFIERS: &'static [&'static str] = &[ + #(#identifiers),* + ]; } } }); + let alt_identifier = self.config.identifier.as_ref().map_or( + quote!(), + |id| quote!(const IDENTIFIER: Option<&'static str> = Some(#id);), + ); + quote! { impl #impl_generics #crate_root::AsnType for #name #ty_generics #where_clause { const TAG: #crate_root::Tag = { @@ -143,7 +159,7 @@ impl Enum { const _: () = assert!(TAG_TREE.is_unique(), #error_message); #return_val }; - + #alt_identifier #constraints_def } @@ -312,18 +328,6 @@ impl Enum { fn encode_choice(&self, generics: &syn::Generics) -> proc_macro2::TokenStream { let crate_root = &self.config.crate_root; - let identifiers = self.variants.iter().map(|v| { - let ident = &v.ident; - let name = &self.name; - - let identifier = syn::LitStr::new(&v.ident.to_string(), proc_macro2::Span::call_site()); - - match &v.fields { - syn::Fields::Named(_) => quote!(#name::#ident { .. } => #identifier), - syn::Fields::Unnamed(_) => quote!(#name::#ident (_) => #identifier), - syn::Fields::Unit => quote!(#name::#ident => #identifier), - } - }); let tags = self.variants.iter().enumerate().map(|(i, v)| { let ident = &v.ident; let name = &self.name; @@ -413,9 +417,6 @@ impl Enum { match self { #(#tags),* }, - match self { - #(#identifiers),* - }, |encoder| match self { #(#variants),* } diff --git a/src/ber/enc.rs b/src/ber/enc.rs index 9e2798b4..a61035f3 100644 --- a/src/ber/enc.rs +++ b/src/ber/enc.rs @@ -359,7 +359,6 @@ impl crate::Encoder for Encoder { &mut self, _: Constraints, _t: Tag, - _i: &str, encode_fn: impl FnOnce(&mut Self) -> Result, ) -> Result { (encode_fn)(self).map(drop) diff --git a/src/enc.rs b/src/enc.rs index 261a07c1..3c5f2bf6 100644 --- a/src/enc.rs +++ b/src/enc.rs @@ -323,7 +323,6 @@ pub trait Encoder { &mut self, constraints: Constraints, tag: Tag, - identifier: &'static str, encode_fn: impl FnOnce(&mut Self) -> Result, ) -> Result; diff --git a/src/jer.rs b/src/jer.rs index f99206a2..a74b8a3b 100644 --- a/src/jer.rs +++ b/src/jer.rs @@ -148,6 +148,25 @@ mod tests { #[rasn(crate_root = "crate", delegate, size("3", extensible))] struct ConstrainedBitString(pub BitString); + #[derive(AsnType, Decode, Encode, Debug, PartialEq)] + #[rasn(automatic_tags)] + #[rasn(crate_root = "crate")] + struct Renamed { + #[rasn(identifier = "so-very")] + very: Integer, + #[rasn(identifier = "re_named")] + renamed: Option, + } + + #[derive(AsnType, Decode, Encode, Debug, Clone, PartialEq)] + #[rasn(automatic_tags, choice)] + #[rasn(crate_root = "crate")] + enum Renumed { + #[rasn(identifier = "test-1")] + #[rasn(size("0..3"))] + Test1(Utf8String), + } + #[test] fn bool() { round_trip_jer!(bool, true, "true"); @@ -288,4 +307,18 @@ mod tests { "{\"a\":{\"very\":{},\"nested\":false}}" ); } + + #[test] + fn with_identifier_annotation() { + round_trip_jer!( + Renamed, + Renamed { + very: 1.into(), + renamed: Some(true), + }, + r#"{"so_very":1,"re_named":true}"# + ); + + round_trip_jer!(Renumed, Renumed::Test1("hel".into()), r#"{"test_1":"hel"}"#); + } } diff --git a/src/jer/de.rs b/src/jer/de.rs index 17dde602..afa9bf94 100644 --- a/src/jer/de.rs +++ b/src/jer/de.rs @@ -101,12 +101,12 @@ impl crate::Decoder for Decoder { let mut field_names = [D::FIELDS, D::EXTENDED_FIELDS.unwrap_or(Fields::empty())] .iter() .flat_map(|f| f.iter()) - .map(|f| f.name) - .collect::>(); + .map(|f| f.name.replace('-', "_")) + .collect::>(); field_names.reverse(); for name in field_names { self.stack - .push(value_map.remove(name).unwrap_or(JsonValue::Null)); + .push(value_map.remove(&name).unwrap_or(JsonValue::Null)); } (decode_fn)(self) @@ -530,7 +530,7 @@ impl Decoder { D::IDENTIFIERS .iter() .enumerate() - .find(|id| id.1.eq_ignore_ascii_case(k)) + .find(|id| id.1.replace('-', "_").eq_ignore_ascii_case(k)) .map(|(i, _)| (i, v)) }) .map_or(Tag::EOC, |(i, v)| { diff --git a/src/jer/enc.rs b/src/jer/enc.rs index 52b5906a..6e8d7c96 100644 --- a/src/jer/enc.rs +++ b/src/jer/enc.rs @@ -8,7 +8,7 @@ use crate::{ }; pub struct Encoder { - stack: alloc::vec::Vec<&'static str>, + stack: alloc::vec::Vec, constructed_stack: alloc::vec::Vec, root_value: Option, } @@ -46,7 +46,7 @@ impl Encoder { .ok_or_else(|| JerEncodeErrorKind::JsonEncoder { msg: "Internal stack mismatch!".into(), })? - .insert(id, value); + .insert(&id, value); } None => { self.root_value = Some(value); @@ -284,7 +284,7 @@ impl crate::Encoder for Encoder { .collect::>(); field_names.reverse(); for name in field_names { - self.stack.push(name); + self.stack.push(name.replace('-', "_")); } self.constructed_stack.push(Object::new()); (encoder_scope)(self)?; @@ -371,18 +371,28 @@ impl crate::Encoder for Encoder { fn encode_choice( &mut self, _c: crate::types::Constraints, - _t: crate::types::Tag, - identifier: &'static str, + tag: crate::types::Tag, encode_fn: impl FnOnce(&mut Self) -> Result, ) -> Result { let variants = variants::Variants::from_slice( &[E::VARIANTS, E::EXTENDED_VARIANTS.unwrap_or(&[])].concat(), ); + + let identifier = variants + .iter() + .enumerate() + .find_map(|(i, &variant_tag)| { + (tag == variant_tag) + .then_some(E::IDENTIFIERS.get(i)) + .flatten() + }) + .ok_or_else(|| crate::error::EncodeError::variant_not_in_choice(self.codec()))?; + if variants.is_empty() { self.update_root_or_constructed(JsonValue::Object(Object::new())) } else { self.constructed_stack.push(Object::new()); - self.stack.push(identifier); + self.stack.push(identifier.replace('-', "_")); (encode_fn)(self)?; let value_map = self.constructed_stack diff --git a/src/per/enc.rs b/src/per/enc.rs index 0538ed37..be692ba6 100644 --- a/src/per/enc.rs +++ b/src/per/enc.rs @@ -1096,7 +1096,6 @@ impl crate::Encoder for Encoder { &mut self, constraints: Constraints, tag: Tag, - _: &str, encode_fn: impl FnOnce(&mut Self) -> Result, ) -> Result { let mut buffer = BitString::new(); diff --git a/src/types.rs b/src/types.rs index aa9b6316..726398e7 100644 --- a/src/types.rs +++ b/src/types.rs @@ -84,6 +84,10 @@ pub trait AsnType { const TAG_TREE: TagTree = TagTree::Leaf(Self::TAG); const CONSTRAINTS: Constraints<'static> = Constraints::NONE; + + /// Identifier of an ASN.1 type as specified in the original specification + /// if not identical with the identifier of `Self` + const IDENTIFIER: Option<&'static str> = None; } /// A `SET` or `SEQUENCE` value. @@ -123,6 +127,9 @@ pub trait Enumerated: Sized + 'static + PartialEq + Copy + core::fmt::Debug { /// present. const EXTENDED_DISCRIMINANTS: Option<&'static [(Self, isize)]>; + /// Identifiers of enum variants + const IDENTIFIERS: &'static [&'static str]; + /// Returns the number of "root" variants for a given type. fn variance() -> usize { Self::VARIANTS.len() diff --git a/src/types/fields.rs b/src/types/fields.rs index 4710926b..eb9d85dd 100644 --- a/src/types/fields.rs +++ b/src/types/fields.rs @@ -58,6 +58,10 @@ impl Fields { pub fn iter(&self) -> impl Iterator + '_ { self.fields.iter().cloned() } + + pub fn identifiers(&self) -> impl Iterator + '_ { + self.fields.iter().map(|f| f.name) + } } impl From> for Fields { diff --git a/tests/derive.rs b/tests/derive.rs index b0467b2b..9aee8df3 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -209,3 +209,42 @@ fn enum_with_encoder_tag_name_variants() { }, } } + +#[test] +fn explicit_identifiers() { + #[derive(AsnType, Encode, Decode)] + #[rasn(choice, identifier = "my-choice")] + enum MyChoice { + #[rasn(identifier = "has-alt-ident")] + HasAltIdent(()), + } + + #[derive(AsnType, Encode, Decode, Debug, PartialEq, Clone, Copy)] + #[rasn(enumerated, identifier = "my-enum")] + enum MyEnum { + #[rasn(identifier = "has-alt-ident")] + HasAltIdent, + } + + #[derive(AsnType, Encode, Decode)] + #[rasn(identifier = "my-struct")] + struct MyStruct { + #[rasn(identifier = "has-alt-ident")] + has_alt_ident: (), + } + + #[derive(AsnType, Encode, Decode)] + #[rasn(identifier = "my-delegate")] + struct MyDelegate(()); + + assert_eq!(MyEnum::IDENTIFIER, Some("my-enum")); + assert_eq!(MyEnum::IDENTIFIERS, ["has-alt-ident"]); + assert_eq!(MyChoice::IDENTIFIER, Some("my-choice")); + assert_eq!(MyChoice::IDENTIFIERS, ["has-alt-ident"]); + assert_eq!(MyStruct::IDENTIFIER, Some("my-struct")); + assert_eq!( + MyStruct::FIELDS.identifiers().collect::>(), + vec!["has-alt-ident"] + ); + assert_eq!(MyDelegate::IDENTIFIER, Some("my-delegate")); +}