Skip to content

Commit c9d0a01

Browse files
committed
move to ast_node
1 parent c0310be commit c9d0a01

File tree

32 files changed

+737
-150
lines changed

32 files changed

+737
-150
lines changed

Cargo.lock

Lines changed: 1 addition & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,6 @@ resolver = "2"
130130
foldhash = "0.1"
131131
precomputed-map = "0.2"
132132
cbor4ii = { git = "https://github.com/quininer/cbor4ii", branch = "f/derive" }
133-
cbor4ii-derive = { git = "https://github.com/quininer/cbor4ii", branch = "f/derive" }
134133

135134
[workspace.metadata.cargo-shear]
136135
# `serde` is used when #[ast_node] is expanded
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
use syn::{ DeriveInput, Data };
2+
use super::{ is_unknown, is_with };
3+
4+
pub fn expand(
5+
DeriveInput {
6+
ident,
7+
data,
8+
..
9+
}: DeriveInput,
10+
) -> syn::ItemImpl {
11+
match data {
12+
Data::Struct(data) => {
13+
let is_named = data.fields.iter().any(|field| field.ident.is_some());
14+
15+
let fields = data.fields.iter()
16+
.enumerate()
17+
.map(|(idx, field)| -> syn::Stmt {
18+
let ty = &field.ty;
19+
let value: syn::Expr = match is_with(&field.attrs) {
20+
Some(with_type) => syn::parse_quote!(<#with_type<#ty> as cbor4ii::core::dec::Decode<'_>>::decode(reader)?.0),
21+
None => syn::parse_quote!(<#ty as cbor4ii::core::dec::Decode<'_>>::decode(reader)?)
22+
};
23+
24+
match field.ident.as_ref() {
25+
Some(name) => syn::parse_quote!{
26+
let #name = #value;
27+
},
28+
None => {
29+
let name = format!("unit{}", idx);
30+
syn::parse_quote!{
31+
let #name = #value;
32+
}
33+
}
34+
}
35+
});
36+
let build_struct = data.fields.iter()
37+
.enumerate()
38+
.map(|(idx, field)| -> syn::FieldValue {
39+
match field.ident.as_ref() {
40+
Some(name) => syn::parse_quote!(#name),
41+
None => {
42+
let name = format!("unit{}", idx);
43+
syn::parse_quote!(#name)
44+
}
45+
}
46+
})
47+
.collect::<syn::punctuated::Punctuated<_, syn::Token![,]>>();
48+
let build_struct: syn::ExprStruct = if is_named {
49+
syn::parse_quote!{
50+
#ident { #build_struct }
51+
}
52+
} else {
53+
syn::parse_quote!{
54+
#ident(#build_struct)
55+
}
56+
};
57+
58+
let count = data.fields.len();
59+
let head: Option<syn::Expr> = (count != 1).then(|| syn::parse_quote!{{
60+
let n = <cbor4ii::core::types::Array<()>>::len(reader)?;
61+
debug_assert_eq!(n, Some(#count));
62+
}});
63+
64+
syn::parse_quote! {
65+
impl<'de> cbor4ii::core::dec::Decode<'de> for #ident {
66+
#[inline]
67+
fn decode<R: cbor4ii::core::dec::Read<'de>>(reader: &mut R)
68+
-> Result<Self, cbor4ii::core::error::DecodeError<R::Error>>
69+
{
70+
#head;
71+
#(#fields)*
72+
Ok(#build_struct)
73+
}
74+
}
75+
}
76+
},
77+
Data::Enum(data) => {
78+
let mut iter = data.variants.iter().peekable();
79+
let mut is_unit = None;
80+
81+
assert!(!data.variants.is_empty(), "empty enums are not allowed");
82+
83+
let unknown_arm: Option<syn::Arm> = if let Some(unknown) =
84+
iter.next_if(|variant| is_unknown(&variant.attrs))
85+
{
86+
let name = &unknown.ident;
87+
assert!(unknown.discriminant.is_none(), "custom discriminant unsupport");
88+
assert!(is_with(&unknown.attrs).is_none(), "unknown member is not allowed with type");
89+
90+
Some(match &unknown.fields {
91+
syn::Fields::Unnamed(fields) => {
92+
match fields.unnamed.len() {
93+
1 => {
94+
is_unit = Some(true);
95+
syn::parse_quote!{
96+
tag => #ident::#name(tag),
97+
}
98+
},
99+
2 => {
100+
is_unit = Some(false);
101+
let val_ty = &fields.unnamed[1].ty;
102+
103+
syn::parse_quote!{
104+
tag => {
105+
let val = <#val_ty as cbor4ii::core::dec::Decode<'_>>::decode(reader)?;
106+
#ident::#name(tag, val)
107+
},
108+
}
109+
},
110+
_ => panic!("unknown member must be a tag and a value")
111+
}
112+
},
113+
_ => panic!("named enum unsupported")
114+
})
115+
} else {
116+
None
117+
};
118+
119+
let fields = iter
120+
.enumerate()
121+
.map(|(idx, field)| -> syn::Arm {
122+
let idx: u32 = idx.try_into().expect("enum tag max 32bit");
123+
let idx = idx + 1; // skip zero
124+
let name = &field.ident;
125+
126+
assert!(field.discriminant.is_none(), "custom discriminant unsupport");
127+
assert!(!is_unknown(&field.attrs), "unknown member must be first");
128+
129+
match &field.fields {
130+
syn::Fields::Unnamed(fields) => {
131+
if fields.unnamed.len() != 1 {
132+
panic!("enum member only allows one field");
133+
}
134+
135+
if *is_unit.get_or_insert(false) != false {
136+
panic!("The number of fields in member must be consistent");
137+
}
138+
let val_ty = &fields.unnamed[0].ty;
139+
let value: syn::Expr = match is_with(&field.attrs) {
140+
Some(with_type) => syn::parse_quote!(<#with_type<#val_ty> as cbor4ii::core::dec::Decode<'_>>::decode(reader)?.0),
141+
None => syn::parse_quote!(<#val_ty as cbor4ii::core::dec::Decode<'_>>::decode(reader)?)
142+
};
143+
144+
syn::parse_quote!{
145+
#idx => {
146+
let val = #value;
147+
#ident::#name(val)
148+
},
149+
}
150+
},
151+
syn::Fields::Unit => {
152+
if *is_unit.get_or_insert(true) != true {
153+
panic!("The number of fields in member must be consistent");
154+
}
155+
assert!(is_with(&field.attrs).is_none(), "unit member is not allowed with type");
156+
157+
syn::parse_quote!{
158+
#idx => #ident::#name,
159+
}
160+
},
161+
syn::Fields::Named(_) => panic!("named enum unsupported")
162+
}
163+
})
164+
.collect::<Vec<_>>();
165+
166+
let unknown_arm = match unknown_arm {
167+
Some(arm) => arm,
168+
None => {
169+
syn::parse_quote!{
170+
_ => {
171+
let err = cbor4ii::core::error::DecodeError::Mismatch {
172+
name: &stringify!(#ident),
173+
found: 0
174+
};
175+
return Err(err);
176+
}
177+
}
178+
}
179+
};
180+
181+
let head: syn::Expr = {
182+
let count: usize = match is_unit {
183+
Some(true) => 1,
184+
Some(false) => 2,
185+
None => panic!()
186+
};
187+
syn::parse_quote!{{
188+
let n = <cbor4ii::core::types::Array<()>>::len(reader)?;
189+
debug_assert_eq!(n, Some(#count));
190+
}}
191+
};
192+
193+
syn::parse_quote!{
194+
impl<'de> cbor4ii::core::dec::Decode<'de> for #ident {
195+
#[inline]
196+
fn decode<R: cbor4ii::core::dec::Read<'de>>(reader: &mut R)
197+
-> Result<Self, cbor4ii::core::error::DecodeError<R::Error>>
198+
{
199+
#head;
200+
let tag = <u32 as cbor4ii::core::dec::Decode<'_>>::decode(reader)?;
201+
let value = match tag {
202+
#(#fields)*
203+
#unknown_arm
204+
};
205+
Ok(value)
206+
}
207+
}
208+
}
209+
},
210+
Data::Union(_) => panic!("union unsupported")
211+
}
212+
}

0 commit comments

Comments
 (0)