Skip to content

Commit a6a653f

Browse files
committed
introduce: Enocde/Decode
move to ast_node make encoding-impl optional fix fmt make css_ast fix tuple fix unknown fix fmt make html_ast make xml_ast make jsdoc fix fmt fix unit tag update visit fix parser make regexp_ast no unknown fix snap fix arbitrary encoding: ignore unknown field fix cfg
1 parent f328e4a commit a6a653f

File tree

109 files changed

+2628
-400
lines changed

Some content is hidden

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

109 files changed

+2628
-400
lines changed

Cargo.lock

Lines changed: 81 additions & 68 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ resolver = "2"
129129

130130
foldhash = "0.1"
131131
precomputed-map = "0.2"
132+
cbor4ii = { git = "https://github.com/quininer/cbor4ii", branch = "f/derive" }
132133

133134
[workspace.metadata.cargo-shear]
134135
# `serde` is used when #[ast_node] is expanded
Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
use syn::{spanned::Spanned, Data, DeriveInput};
2+
3+
use super::{is_unknown, is_with, EnumType};
4+
5+
pub fn expand(DeriveInput { ident, data, .. }: DeriveInput) -> syn::ItemImpl {
6+
match data {
7+
Data::Struct(data) => {
8+
let is_named = data.fields.iter().any(|field| field.ident.is_some());
9+
let names = data
10+
.fields
11+
.iter()
12+
.enumerate()
13+
.map(|(idx, field)| match field.ident.as_ref() {
14+
Some(name) => name.clone(),
15+
None => {
16+
let name = format!("unit{idx}");
17+
syn::Ident::new(&name, field.span())
18+
}
19+
})
20+
.collect::<Vec<_>>();
21+
22+
let fields = data.fields.iter()
23+
.zip(names.iter())
24+
.map(|(field, field_name)| -> syn::Stmt {
25+
let ty = &field.ty;
26+
let value: syn::Expr = match is_with(&field.attrs) {
27+
Some(with_type) => syn::parse_quote!(<#with_type<#ty> as cbor4ii::core::dec::Decode<'_>>::decode(reader)?.0),
28+
None => syn::parse_quote!(<#ty as cbor4ii::core::dec::Decode<'_>>::decode(reader)?)
29+
};
30+
31+
syn::parse_quote!{
32+
let #field_name = #value;
33+
}
34+
});
35+
let build_struct: syn::Expr = if is_named {
36+
syn::parse_quote! { #ident { #(#names),* } }
37+
} else {
38+
syn::parse_quote! { #ident ( #(#names),* ) }
39+
};
40+
41+
let count = data.fields.len();
42+
let head: Option<syn::Stmt> = (count != 1).then(|| {
43+
syn::parse_quote! {
44+
let len = <cbor4ii::core::types::Array<()>>::len(reader)?.unwrap();
45+
}
46+
});
47+
let tail: Option<syn::Stmt> = head.is_some().then(|| {
48+
syn::parse_quote! {
49+
// ignore unknown field
50+
for _ in 0..(len - #count) {
51+
cbor4ii::core::dec::IgnoredAny::decode(reader)?;
52+
}
53+
}
54+
});
55+
56+
syn::parse_quote! {
57+
impl<'de> cbor4ii::core::dec::Decode<'de> for #ident {
58+
#[inline]
59+
fn decode<R: cbor4ii::core::dec::Read<'de>>(reader: &mut R)
60+
-> Result<Self, cbor4ii::core::error::DecodeError<R::Error>>
61+
{
62+
#head;
63+
let value = {
64+
#(#fields)*
65+
#build_struct
66+
};
67+
#tail
68+
Ok(value)
69+
}
70+
}
71+
}
72+
}
73+
Data::Enum(data) => {
74+
let enum_type = data.variants.iter().filter(|v| !is_unknown(&v.attrs)).fold(
75+
None,
76+
|mut sum, next| {
77+
let ty = match &next.fields {
78+
syn::Fields::Named(_) => EnumType::Struct,
79+
syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => EnumType::One,
80+
syn::Fields::Unit => EnumType::Unit,
81+
syn::Fields::Unnamed(_) => {
82+
panic!("more than 1 unnamed member field are not allowed")
83+
}
84+
};
85+
match (*sum.get_or_insert(ty), ty) {
86+
(EnumType::Struct, EnumType::Struct)
87+
| (EnumType::Struct, EnumType::Unit)
88+
| (EnumType::Unit, EnumType::Unit)
89+
| (EnumType::One, EnumType::One) => (),
90+
(EnumType::Unit, EnumType::One)
91+
| (EnumType::One, EnumType::Unit)
92+
| (_, EnumType::Struct) => sum = Some(EnumType::Struct),
93+
_ => panic!("enum member types must be consistent: {:?}", (sum, ty)),
94+
}
95+
sum
96+
},
97+
);
98+
let enum_type = enum_type.expect("enum cannot be empty");
99+
let mut iter = data.variants.iter().peekable();
100+
101+
let unknown_arm: Option<syn::Arm> = iter.next_if(|variant| is_unknown(&variant.attrs))
102+
.map(|unknown| {
103+
let name = &unknown.ident;
104+
assert!(
105+
unknown.discriminant.is_none(),
106+
"unknown member is not allowed custom discriminant"
107+
);
108+
assert!(
109+
is_with(&unknown.attrs).is_none(),
110+
"unknown member is not allowed with type"
111+
);
112+
113+
match &unknown.fields {
114+
syn::Fields::Unnamed(fields) => match fields.unnamed.len() {
115+
1 => {
116+
assert_eq!(enum_type, EnumType::Unit);
117+
syn::parse_quote! {
118+
tag => #ident::#name(tag),
119+
}
120+
}
121+
2 => {
122+
assert_eq!(enum_type, EnumType::One);
123+
let val_ty = &fields.unnamed[1].ty;
124+
syn::parse_quote! {
125+
tag => {
126+
let tag: u32 = tag.try_into().map_err(|_| cbor4ii::core::error::DecodeError::CastOverflow {
127+
name: &"tag",
128+
})?;
129+
let val = <#val_ty as cbor4ii::core::dec::Decode<'_>>::decode(reader)?;
130+
#ident::#name(tag, val)
131+
},
132+
}
133+
}
134+
_ => panic!("unknown member must be a tag and a value"),
135+
},
136+
_ => panic!("named enum unsupported"),
137+
}
138+
});
139+
140+
if matches!(enum_type, EnumType::Struct) {
141+
assert!(
142+
unknown_arm.is_none(),
143+
"struct enum does not allow unknown variants"
144+
);
145+
}
146+
147+
let mut discriminant: u32 = 0;
148+
let fields = iter
149+
.map(|field| -> syn::Arm {
150+
match field.discriminant.as_ref() {
151+
Some((_, syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Int(lit), .. }))) => {
152+
discriminant = lit.base10_parse::<u32>().unwrap();
153+
},
154+
Some(_) => panic!("unsupported discriminant type"),
155+
None => (),
156+
};
157+
discriminant += 1;
158+
let idx = discriminant as u64;
159+
let name = &field.ident;
160+
161+
assert!(!is_unknown(&field.attrs), "unknown member must be first");
162+
163+
match enum_type {
164+
EnumType::Unit => {
165+
assert!(is_with(&field.attrs).is_none(), "unit member is not allowed with type");
166+
syn::parse_quote!{
167+
#idx => #ident::#name,
168+
}
169+
},
170+
EnumType::One => {
171+
let val_ty = &field.fields.iter().next().unwrap().ty;
172+
let value: syn::Expr = match is_with(&field.attrs) {
173+
Some(with_type)
174+
=> syn::parse_quote!(<#with_type<#val_ty> as cbor4ii::core::dec::Decode<'_>>::decode(reader)?.0),
175+
None => syn::parse_quote!(<#val_ty as cbor4ii::core::dec::Decode<'_>>::decode(reader)?)
176+
};
177+
178+
syn::parse_quote!{
179+
#idx => #ident::#name(#value),
180+
}
181+
},
182+
EnumType::Struct => {
183+
let is_named = field.fields.iter().all(|field| field.ident.is_some());
184+
let names = field.fields.iter()
185+
.enumerate()
186+
.map(|(idx, field)| match field.ident.as_ref() {
187+
Some(name) => name.clone(),
188+
None => {
189+
let name = format!("unit{idx}");
190+
syn::Ident::new(&name, field.span())
191+
}
192+
})
193+
.collect::<Vec<_>>();
194+
let count = field.fields.len();
195+
196+
let stmt = field.fields.iter()
197+
.zip(names.iter())
198+
.map(|(field, field_name)| -> syn::Stmt {
199+
let val_ty = &field.ty;
200+
match is_with(&field.attrs) {
201+
Some(with_type) => syn::parse_quote!{
202+
let #field_name = <#with_type<#val_ty> as cbor4ii::core::dec::Decode<'_>>::decode(reader)?.0;
203+
},
204+
None => syn::parse_quote!{
205+
let #field_name = <#val_ty as cbor4ii::core::dec::Decode<'_>>::decode(reader)?;
206+
}
207+
}
208+
});
209+
let build_struct: syn::Expr = if is_named {
210+
syn::parse_quote! { #ident::#name { #(#names),* } }
211+
} else {
212+
syn::parse_quote! { #ident::#name ( #(#names),* ) }
213+
};
214+
215+
syn::parse_quote!{
216+
#idx => {
217+
let len = cbor4ii::core::types::Array::len(reader)?.unwrap();
218+
let value = {
219+
#(#stmt)*
220+
#build_struct
221+
};
222+
223+
// ignore unknown field
224+
for _ in 0..(len - #count) {
225+
cbor4ii::core::dec::IgnoredAny::decode(reader)?;
226+
}
227+
228+
value
229+
},
230+
}
231+
}
232+
}
233+
});
234+
235+
let unknown_arm = match unknown_arm {
236+
Some(arm) => arm,
237+
None => {
238+
syn::parse_quote! {
239+
_ => {
240+
let err = cbor4ii::core::error::DecodeError::Mismatch {
241+
name: &stringify!(#ident),
242+
found: 0
243+
};
244+
return Err(err);
245+
}
246+
}
247+
}
248+
};
249+
250+
let tag: syn::Stmt = if matches!(enum_type, EnumType::Unit) {
251+
syn::parse_quote!{
252+
let tag = <u64 as cbor4ii::core::dec::Decode<'_>>::decode(reader)?;
253+
}
254+
} else {
255+
syn::parse_quote!{
256+
let tag = <cbor4ii::core::types::Tag<()>>::tag(reader)?;
257+
}
258+
};
259+
260+
syn::parse_quote! {
261+
impl<'de> cbor4ii::core::dec::Decode<'de> for #ident {
262+
#[inline]
263+
fn decode<R: cbor4ii::core::dec::Read<'de>>(reader: &mut R)
264+
-> Result<Self, cbor4ii::core::error::DecodeError<R::Error>>
265+
{
266+
#tag
267+
let value = match tag {
268+
#(#fields)*
269+
#unknown_arm
270+
};
271+
Ok(value)
272+
}
273+
}
274+
}
275+
}
276+
Data::Union(_) => panic!("union unsupported"),
277+
}
278+
}

0 commit comments

Comments
 (0)