Skip to content

Commit c7f7cd2

Browse files
6d7aXAMPPRocky
andauthored
Feat/xml encoding rules (#416)
* feat(xer): pass identifier to encode functions * test(xer): add round trip tests * refactor(types): remove constructed asntype bound * fix(macros): generate choice implementation for encode_with_identifier * test(xer): add round trip tests * chore: bump compiler version * fix(xer): fix typo in codec impl Co-authored-by: XAMPPRocky <[email protected]> * refactor: create `Identifier` newtype with consts * style: fmt and clippy * test(xer): un-flake SET OF tests * style: implement clippy suggestions --------- Co-authored-by: XAMPPRocky <[email protected]>
1 parent d97c44d commit c7f7cd2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+4382
-311
lines changed

Cargo.lock

+10-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,13 @@ once_cell = { version = "1.20.2", default-features = false, features = [
9191
"race",
9292
"alloc",
9393
] }
94-
rasn-compiler = { version = "0.5.3", optional = true }
94+
rasn-compiler = { version = "0.7", optional = true }
9595
rasn-derive = { version = "0.24", path = "macros" }
9696
snafu = { version = "0.8.5", default-features = false, features = [
9797
"rust_1_81",
9898
] }
9999
serde_json = { version = "1", default-features = false, features = ["alloc"] }
100+
xml-no-std = "0.8.19"
100101

101102
[dev-dependencies]
102103
criterion = { version = "0.5.1", default-features = false, features = [

README.md

+10-3
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@
88
Welcome to `rasn` (pronounced "raisin"), a safe `#[no_std]` ASN.1 codec framework.
99
That enables you to safely create, share, and handle ASN.1 data types from and to different encoding rules. If you are unfamiliar with ASN.1 and encoding formats like BER/DER, I would recommend reading [*"A Warm Welcome to ASN.1 and DER"*][lenc] by Let's Encrypt as a quick introduction before continuing. In short it is an "Interface Description Language" (and data model) with a set of encoding formats (called rules) for that model. It was originally designed in the late 1980s and is used throughout the industry especially in telecommunications and cryptography.
1010

11+
The [`rasn` compiler][compiler] can be used to generate `rasn` bindings for ASN.1 modules.
12+
1113
[ghs]: https://github.com/sponsors/XAMPPRocky
1214
[lenc]: https://letsencrypt.org/docs/a-warm-welcome-to-asn1-and-der/
15+
[compiler]: https://github.com/librasn/compiler
1316

1417
## Features
1518

@@ -37,6 +40,7 @@ The encoder and decoder have been written in 100% safe Rust and fuzzed with [Ame
3740
- JSON Encoding Rules (JER)
3841
- Octet Encoding Rules (OER)
3942
- Canonical Octet Encoding Rules (COER)
43+
- XML Encoding Rules (XER)
4044

4145
[bun]: https://aflplus.plus
4246

@@ -98,7 +102,10 @@ Next is the `Decode` and `Encode` traits. These are mirrors of each other and bo
98102
```rust
99103
# use rasn::{AsnType, types::{Constructed, fields::{Field, Fields}}};
100104
# struct Person { name: Utf8String, age: Integer }
101-
# impl AsnType for Person { const TAG: Tag = Tag::SEQUENCE; }
105+
# impl AsnType for Person {
106+
# const TAG: Tag = Tag::SEQUENCE;
107+
# const IDENTIFIER: Identifier = Identifier(Some("Person"));
108+
# }
102109
# impl Constructed<2, 0> for Person {
103110
# const FIELDS: Fields<2> = Fields::from_static([
104111
# Field::new_required(0, Utf8String::TAG, Utf8String::TAG_TREE, "age"),
@@ -119,13 +126,13 @@ impl Decode for Person {
119126
}
120127

121128
impl Encode for Person {
122-
fn encode_with_tag_and_constraints<'encoder, E: Encoder<'encoder>>(&self, encoder: &mut E, tag: Tag, constraints: Constraints) -> Result<(), E::Error> {
129+
fn encode_with_tag_and_constraints<'encoder, E: Encoder<'encoder>>(&self, encoder: &mut E, tag: Tag, constraints: Constraints, identifier: Identifier) -> Result<(), E::Error> {
123130
// Accepts a closure that encodes the contents of the sequence.
124131
encoder.encode_sequence::<2, 0, Self, _>(tag, |encoder| {
125132
self.age.encode(encoder)?;
126133
self.name.encode(encoder)?;
127134
Ok(())
128-
})?;
135+
}, identifier)?;
129136

130137
Ok(())
131138
}

benches/integer.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
// Wrap with black_box to prevent the compiler from optimizing out the code
88

99
use criterion::{black_box, criterion_group, criterion_main, Criterion};
10-
use rasn::{ber, oer, uper};
10+
use rasn::{ber, oer, uper, xer};
1111

1212
#[allow(non_camel_case_types, non_snake_case, non_upper_case_globals, unused)]
1313
pub mod world3d {
@@ -154,10 +154,12 @@ macro_rules! rasn_dec_fn {
154154
rasn_enc_fn!(uper_rasn_enc, uper);
155155
rasn_enc_fn!(oer_rasn_enc, oer);
156156
rasn_enc_fn!(ber_rasn_enc, ber);
157+
rasn_enc_fn!(xer_rasn_enc, xer);
157158

158159
rasn_dec_fn!(uper_rasn_dec, uper);
159160
rasn_dec_fn!(oer_rasn_dec, oer);
160161
rasn_dec_fn!(ber_rasn_dec, ber);
162+
rasn_dec_fn!(xer_rasn_dec, xer);
161163

162164
criterion_group!(
163165
benches,
@@ -166,6 +168,8 @@ criterion_group!(
166168
oer_rasn_enc,
167169
oer_rasn_dec,
168170
ber_rasn_enc,
169-
ber_rasn_dec
171+
ber_rasn_dec,
172+
xer_rasn_enc,
173+
xer_rasn_dec
170174
);
171175
criterion_main!(benches);

macros/macros_impl/src/asn_type.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ pub fn derive_struct_impl(
88
container: syn::DataStruct,
99
config: &Config,
1010
) -> syn::Result<proc_macro2::TokenStream> {
11+
let name_literal = name.to_string();
1112
let crate_root = &config.crate_root;
1213
let tag = config.tag_for_struct(&container.fields);
1314
let field_configs = container
@@ -90,8 +91,8 @@ pub fn derive_struct_impl(
9091
let constraints_def = config.constraints.const_static_def(crate_root);
9192

9293
let alt_identifier = config.identifier.as_ref().map_or(
93-
quote!(),
94-
|id| quote!(const IDENTIFIER: Option<&'static str> = Some(#id);),
94+
quote!(const IDENTIFIER: #crate_root::types::Identifier = #crate_root::types::Identifier(Some(#name_literal));),
95+
|id| quote!(const IDENTIFIER: #crate_root::types::Identifier = #crate_root::types::Identifier(Some(#id));),
9596
);
9697

9798
Ok(quote! {

macros/macros_impl/src/config.rs

+32-11
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::{ext::TypeExt, tag::Tag};
2+
use proc_macro2::Span;
23
use quote::ToTokens;
34
use std::ops::Deref;
45
use syn::spanned::Spanned;
@@ -810,10 +811,20 @@ impl<'a> FieldConfig<'a> {
810811
.as_ref()
811812
.map(|name| quote!(#name))
812813
.unwrap_or_else(|| quote!(#i));
813-
let mut ty = self.field.ty.clone();
814814
let crate_root = &self.container_config.crate_root;
815+
let identifier = self
816+
.identifier
817+
.clone()
818+
.or(self
819+
.field
820+
.ident
821+
.as_ref()
822+
.map(|i| LitStr::new(&i.to_string(), Span::call_site())))
823+
.map(|i| quote!(#crate_root::types::Identifier(Some(#i))))
824+
.unwrap_or(quote!(#crate_root::types::Identifier::EMPTY));
825+
let mut ty = self.field.ty.clone();
815826
ty.strip_lifetimes();
816-
let default_fn = self.default_fn();
827+
let default_fn = self.default_fn().map(|d| quote!(#d,));
817828
let has_generics = !type_params.is_empty() && {
818829
if let Type::Path(ref ty) = ty {
819830
ty.path.segments.iter().any(|seg| {
@@ -847,18 +858,19 @@ impl<'a> FieldConfig<'a> {
847858
let encode = if self.tag.is_some() || self.container_config.automatic_tags {
848859
if self.tag.as_ref().is_some_and(|tag| tag.is_explicit()) {
849860
// Note: encoder must be aware if the field is optional and present, so we should not do the presence check on this level
850-
quote!(encoder.encode_explicit_prefix(#tag, &self.#field)?;)
861+
quote!(encoder.encode_explicit_prefix(#tag, &self.#field, #identifier)?;)
851862
} else if self.extension_addition {
852863
quote!(
853864
#constraint_def
854865
encoder.encode_extension_addition(
855866
#tag,
856867
#constraint_name,
857-
&#this #field
868+
&#this #field,
869+
#identifier,
858870
)?;
859871
)
860872
} else if self.extension_addition_group {
861-
quote!(encoder.encode_extension_addition_group(#this #field.as_ref())?;)
873+
quote!(encoder.encode_extension_addition_group(#this #field.as_ref(), #identifier)?;)
862874
} else {
863875
match (self.constraints.has_constraints(), self.default.is_some()) {
864876
(true, true) => {
@@ -869,6 +881,7 @@ impl<'a> FieldConfig<'a> {
869881
#constraint_name,
870882
&#this #field,
871883
#default_fn
884+
#identifier,
872885
)?;
873886
)
874887
}
@@ -880,13 +893,16 @@ impl<'a> FieldConfig<'a> {
880893
#tag,
881894
#constraint_name,
882895
#default_fn
896+
#identifier,
883897
)?;
884898
)
885899
}
886900
(false, true) => {
887-
quote!(encoder.encode_default_with_tag(#tag, &#this #field, #default_fn)?;)
901+
quote!(encoder.encode_default_with_tag(#tag, &#this #field, #default_fn #identifier)?;)
902+
}
903+
(false, false) => {
904+
quote!(#this #field.encode_with_tag_and_identifier(encoder, #tag, #identifier)?;)
888905
}
889-
(false, false) => quote!(#this #field.encode_with_tag(encoder, #tag)?;),
890906
}
891907
}
892908
} else if self.extension_addition {
@@ -895,11 +911,12 @@ impl<'a> FieldConfig<'a> {
895911
encoder.encode_extension_addition(
896912
#tag,
897913
#constraint_name,
898-
&#this #field
914+
&#this #field,
915+
#identifier,
899916
)?;
900917
)
901918
} else if self.extension_addition_group {
902-
quote!(encoder.encode_extension_addition_group(#this #field.as_ref())?;)
919+
quote!(encoder.encode_extension_addition_group(#this #field.as_ref(), #identifier)?;)
903920
} else {
904921
match (self.constraints.has_constraints(), self.default.is_some()) {
905922
(true, true) => {
@@ -909,19 +926,23 @@ impl<'a> FieldConfig<'a> {
909926
#constraint_name,
910927
&#this #field,
911928
#default_fn
929+
#identifier,
912930
)?;
913931
)
914932
}
915933
(true, false) => {
916934
quote!(
917935
#constraint_def
918-
#this #field.encode_with_constraints(
936+
#this #field.encode_with_constraints_and_identifier(
919937
encoder,
920938
#constraint_name,
939+
#identifier,
921940
)?;
922941
)
923942
}
924-
(false, true) => quote!(encoder.encode_default(&#this #field, #default_fn)?;),
943+
(false, true) => {
944+
quote!(encoder.encode_default(&#this #field, #default_fn #identifier)?;)
945+
}
925946
(false, false) => quote!(#this #field.encode(encoder)?;),
926947
}
927948
};

macros/macros_impl/src/decode.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use quote::ToTokens;
12
use syn::Fields;
23

34
use crate::config::*;
@@ -11,6 +12,7 @@ pub fn derive_struct_impl(
1112
) -> syn::Result<proc_macro2::TokenStream> {
1213
let mut list = vec![];
1314
let crate_root = &config.crate_root;
15+
let crate_root_literal = crate_root.to_token_stream().to_string();
1416
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
1517

1618
let field_configs = container
@@ -100,7 +102,7 @@ pub fn derive_struct_impl(
100102
name.clone(),
101103
quote! {
102104
#[derive(#crate_root::AsnType, #crate_root::Decode, #crate_root::Encode)]
103-
#[rasn(delegate)]
105+
#[rasn(delegate, crate_root = #crate_root_literal)]
104106
#tag_attr
105107
#constraints
106108
pub struct #name(#ty);
@@ -113,7 +115,7 @@ pub fn derive_struct_impl(
113115

114116
let choice_def = quote! {
115117
#[derive(#crate_root::AsnType, #crate_root::Decode, #crate_root::Encode)]
116-
#[rasn(choice)]
118+
#[rasn(choice, crate_root = #crate_root_literal)]
117119
enum #choice_name {
118120
#(#field_type_names(#field_type_names)),*
119121
}
@@ -144,15 +146,15 @@ pub fn derive_struct_impl(
144146
let set_field_impl = if config.extension_addition || config.extension_addition_group {
145147
quote! {
146148
if #ident.is_some() {
147-
return Err(rasn::de::Error::duplicate_field(stringify!(#ident), codec))
149+
return Err(#crate_root::de::Error::duplicate_field(stringify!(#ident), codec))
148150
} else {
149151
#ident = value.0;
150152
}
151153
}
152154
} else {
153155
quote! {
154156
if #ident.replace(value.0).is_some() {
155-
return Err(rasn::de::Error::duplicate_field(stringify!(#ident), codec))
157+
return Err(#crate_root::de::Error::duplicate_field(stringify!(#ident), codec))
156158
}
157159
}
158160
};

0 commit comments

Comments
 (0)