Skip to content

Commit 6ab7a40

Browse files
committed
prototype of new derive syntax
1 parent 4947331 commit 6ab7a40

File tree

6 files changed

+719
-682
lines changed

6 files changed

+719
-682
lines changed

ublox_derive/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ proc-macro = true
1212
proc-macro2 = "1.0"
1313
quote = "1.0"
1414
syn = { version = "1.0.14", features = ["extra-traits"] }
15-
proc-macro-error = "0.4.8"
15+
heck = "0.3.1"

ublox_derive/src/input.rs

+360
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,360 @@
1+
use crate::types::{
2+
PackDesc, PackField, PackFieldBitflagItemValue, PackFieldEnum, PackFieldEnumItemValue,
3+
PackFieldFlagValue, PackFieldFlags, PackFieldMap, PackFieldRepr, PackHeader,
4+
};
5+
use heck::CamelCase;
6+
use quote::ToTokens;
7+
use syn::{parse::Parse, spanned::Spanned, Attribute, Error, Ident, Token, Type};
8+
9+
pub fn parse_packet_description(input: syn::DeriveInput) -> syn::Result<PackDesc> {
10+
let struct_name = &input.ident;
11+
12+
const REQUIRED_SUFFIX: &str = "Raw";
13+
let name = struct_name.to_string();
14+
if !name.ends_with(REQUIRED_SUFFIX) {
15+
return Err(Error::new(
16+
input.ident.span(),
17+
format!(
18+
"Invalid name \"{}\", should ends with \"{}\"",
19+
struct_name, REQUIRED_SUFFIX
20+
),
21+
));
22+
}
23+
let name = &name[0..(name.len() - REQUIRED_SUFFIX.len())];
24+
25+
trace!("attrs: {:?}", input.attrs);
26+
validate_has_repr_packed(&input.attrs, &struct_name)?;
27+
let header = parse_ubx_attr(&input.attrs, &struct_name)?;
28+
let struct_comment = extract_item_comment(&input.attrs)?;
29+
30+
let struct_data = match input.data {
31+
syn::Data::Struct(x) => x,
32+
_ => return Err(Error::new(input.span(), "Should be struct")),
33+
};
34+
let fields = parse_fields(struct_data)?;
35+
36+
Ok(PackDesc {
37+
name: name.to_string(),
38+
header,
39+
comment: struct_comment,
40+
fields,
41+
})
42+
}
43+
44+
fn validate_has_repr_packed(attrs: &[Attribute], struct_name: &Ident) -> syn::Result<()> {
45+
let attr = attrs
46+
.iter()
47+
.find(|a| a.path.is_ident("repr"))
48+
.ok_or_else(|| {
49+
Error::new(
50+
struct_name.span(),
51+
format!("No repr(packed) for payload struct {}", struct_name),
52+
)
53+
})?;
54+
if attr.into_token_stream().to_string() != "# [repr (packed)]" {
55+
return Err(Error::new(attr.span(), "Expect repr(packed) here"));
56+
}
57+
58+
Ok(())
59+
}
60+
61+
fn parse_ubx_attr(attrs: &[Attribute], struct_name: &Ident) -> syn::Result<PackHeader> {
62+
let attr = attrs
63+
.iter()
64+
.find(|a| a.path.is_ident("ubx"))
65+
.ok_or_else(|| {
66+
Error::new(
67+
struct_name.span(),
68+
format!("No ubx attribute for payload struct {}", struct_name),
69+
)
70+
})?;
71+
let meta = attr.parse_meta()?;
72+
trace!("parse_ubx_attr: ubx meta {:?}", meta);
73+
let meta = match meta {
74+
syn::Meta::List(x) => x,
75+
_ => return Err(Error::new(meta.span(), "Invalid ubx attribute syntax")),
76+
};
77+
78+
let mut class = None;
79+
let mut id = None;
80+
let mut fixed_len = None;
81+
82+
for e in &meta.nested {
83+
match e {
84+
syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue {
85+
path, lit, ..
86+
})) => {
87+
if path.is_ident("class") {
88+
if class.is_some() {
89+
return Err(Error::new(e.span(), "Duplicate \"class\" attribute"));
90+
}
91+
class = match lit {
92+
syn::Lit::Int(x) => Some(x.base10_parse::<u8>()?),
93+
_ => return Err(Error::new(lit.span(), "Should be integer literal")),
94+
};
95+
} else if path.is_ident("id") {
96+
if id.is_some() {
97+
return Err(Error::new(e.span(), "Duplicate \"id\" attribute"));
98+
}
99+
id = match lit {
100+
syn::Lit::Int(x) => Some(x.base10_parse::<u8>()?),
101+
_ => return Err(Error::new(lit.span(), "Should be integer literal")),
102+
};
103+
} else if path.is_ident("fixed_len") {
104+
if fixed_len.is_some() {
105+
return Err(Error::new(e.span(), "Duplicate \"fixed_len\" attribute"));
106+
}
107+
fixed_len = match lit {
108+
syn::Lit::Int(x) => Some(x.base10_parse::<u16>()?),
109+
_ => return Err(Error::new(lit.span(), "Should be integer literal")),
110+
};
111+
} else {
112+
return Err(Error::new(path.span(), "Unsupported attribute"));
113+
}
114+
}
115+
_ => return Err(Error::new(e.span(), "Unsupported attribute")),
116+
}
117+
}
118+
let class = class.ok_or_else(|| Error::new(meta.span(), "No \"class\" attribute"))?;
119+
let id = id.ok_or_else(|| Error::new(meta.span(), "No \"id\" attribute"))?;
120+
121+
Ok(PackHeader {
122+
class,
123+
id,
124+
fixed_len,
125+
})
126+
}
127+
128+
fn extract_item_comment(attrs: &[Attribute]) -> syn::Result<String> {
129+
let mut doc_comments = String::new();
130+
for a in attrs {
131+
if a.path.is_ident("doc") {
132+
let meta = a.parse_meta()?;
133+
match meta {
134+
syn::Meta::NameValue(syn::MetaNameValue { lit, .. }) => {
135+
let lit = match lit {
136+
syn::Lit::Str(s) => s,
137+
_ => return Err(Error::new(lit.span(), "Invalid comment")),
138+
};
139+
doc_comments.push_str(&lit.value());
140+
}
141+
_ => return Err(Error::new(a.span(), "Invalid comments")),
142+
}
143+
}
144+
}
145+
Ok(doc_comments)
146+
}
147+
148+
fn parse_fields(struct_data: syn::DataStruct) -> syn::Result<Vec<PackField>> {
149+
let fields = match struct_data.fields {
150+
syn::Fields::Named(x) => x,
151+
_ => {
152+
return Err(Error::new(
153+
struct_data.fields.span(),
154+
"Unsupported fields format",
155+
));
156+
}
157+
};
158+
let mut ret = Vec::with_capacity(fields.named.len());
159+
for f in fields.named {
160+
let f_sp = f.span();
161+
let syn::Field {
162+
ident: name,
163+
attrs,
164+
ty,
165+
..
166+
} = f;
167+
let size_bytes = field_size_bytes(&ty)?;
168+
let name = name.ok_or_else(|| Error::new(f_sp, "No field name"))?;
169+
let mut repr = PackFieldRepr::Plain;
170+
let comment = extract_item_comment(&attrs)?;
171+
for a in attrs {
172+
if !a.path.is_ident("doc") {
173+
match repr {
174+
PackFieldRepr::Plain => (),
175+
_ => return Err(Error::new(a.span(), "Two attributes for the same field")),
176+
}
177+
repr = a.parse_args()?;
178+
}
179+
}
180+
let name_camel_case = name.to_string().to_camel_case();
181+
let field_name_as_type: Type = syn::parse_str(&name_camel_case).map_err(|err| {
182+
Error::new(
183+
name.span(),
184+
format!("can not parse {} as type: {}", name_camel_case, err),
185+
)
186+
})?;
187+
188+
ret.push(PackField {
189+
name,
190+
ty,
191+
repr,
192+
comment,
193+
field_name_as_type,
194+
size_bytes,
195+
});
196+
}
197+
198+
Ok(ret)
199+
}
200+
201+
mod kw {
202+
syn::custom_keyword!(bitflags);
203+
syn::custom_keyword!(map_type);
204+
syn::custom_keyword!(scale);
205+
syn::custom_keyword!(alias);
206+
}
207+
208+
impl Parse for PackFieldRepr {
209+
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
210+
let lookahead = input.lookahead1();
211+
if lookahead.peek(kw::bitflags) {
212+
input.parse::<kw::bitflags>()?;
213+
let mut name = None;
214+
if input.peek(Ident) {
215+
name = Some(input.parse::<Type>()?);
216+
}
217+
let content;
218+
let _brace_token = syn::braced!(content in input);
219+
220+
Ok(PackFieldRepr::Flags(PackFieldFlags {
221+
explicit_name: name,
222+
values: content.parse_terminated(PackFieldBitflagItemValue::parse)?,
223+
}))
224+
} else if lookahead.peek(Token![enum]) {
225+
input.parse::<Token![enum]>()?;
226+
let mut name = None;
227+
if input.peek(Ident) {
228+
name = Some(input.parse::<Type>()?);
229+
}
230+
231+
let content;
232+
let _brace_token = syn::braced!(content in input);
233+
Ok(PackFieldRepr::Enum(PackFieldEnum {
234+
explicit_name: name,
235+
values: content.parse_terminated(PackFieldEnumItemValue::parse)?,
236+
}))
237+
} else if lookahead.peek(kw::map_type)
238+
|| lookahead.peek(kw::scale)
239+
|| lookahead.peek(kw::alias)
240+
{
241+
let mut map = PackFieldMap::none();
242+
243+
if input.peek(kw::map_type) {
244+
input.parse::<kw::map_type>()?;
245+
input.parse::<Token![=]>()?;
246+
map.out_type = Some(input.parse()?);
247+
}
248+
if input.peek(Token![,]) {
249+
input.parse::<Token![,]>()?;
250+
}
251+
if input.peek(kw::scale) {
252+
input.parse::<kw::scale>()?;
253+
input.parse::<Token![=]>()?;
254+
map.scale = Some(input.parse()?);
255+
}
256+
if input.peek(Token![,]) {
257+
input.parse::<Token![,]>()?;
258+
}
259+
if input.peek(kw::alias) {
260+
input.parse::<kw::alias>()?;
261+
input.parse::<Token![=]>()?;
262+
map.alias = Some(input.parse()?);
263+
}
264+
265+
assert!(!map.is_none());
266+
267+
Ok(PackFieldRepr::Map(map))
268+
} else {
269+
Err(lookahead.error())
270+
}
271+
}
272+
}
273+
274+
impl Parse for PackFieldEnumItemValue {
275+
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
276+
let comment: Comment = input.parse()?;
277+
Ok(Self {
278+
comment: comment.0,
279+
name: input.parse()?,
280+
_eq_token: input.parse()?,
281+
value: input.parse()?,
282+
})
283+
}
284+
}
285+
286+
impl Parse for PackFieldBitflagItemValue {
287+
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
288+
let comment: Comment = input.parse()?;
289+
Ok(Self {
290+
comment: comment.0,
291+
name: input.parse()?,
292+
_eq_token: input.parse()?,
293+
value: input.parse()?,
294+
})
295+
}
296+
}
297+
298+
impl Parse for PackFieldFlagValue {
299+
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
300+
let ident = input.parse::<Ident>()?;
301+
let value = ident.to_string();
302+
if value.starts_with("bit") {
303+
let number_str = &value[3..];
304+
let n: u8 = number_str.parse().map_err(|err| {
305+
Error::new(
306+
ident.span(),
307+
format!("Can not parse {} as number: {}", number_str, err),
308+
)
309+
})?;
310+
Ok(PackFieldFlagValue::Bit(n))
311+
} else if value.starts_with("mask") {
312+
let number_str = &value[4..];
313+
let n: u64 = number_str.parse().map_err(|err| {
314+
Error::new(
315+
ident.span(),
316+
format!("Can not parse {} as number: {}", number_str, err),
317+
)
318+
})?;
319+
Ok(PackFieldFlagValue::Mask(n))
320+
} else {
321+
Err(Error::new(ident.span(), "Expect bitX or maskX here"))
322+
}
323+
}
324+
}
325+
326+
struct Comment(String);
327+
328+
impl Parse for Comment {
329+
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
330+
if input.peek(Token![#]) && input.peek2(syn::token::Bracket) && input.peek3(Ident) {
331+
let attrs = input.call(Attribute::parse_outer)?;
332+
333+
Ok(Comment(extract_item_comment(&attrs)?))
334+
} else {
335+
Ok(Comment(String::new()))
336+
}
337+
}
338+
}
339+
340+
fn field_size_bytes(ty: &Type) -> syn::Result<Option<usize>> {
341+
//TODO: make this array static
342+
let valid_types: [(Type, usize); 8] = [
343+
(syn::parse_quote!(u8), 1),
344+
(syn::parse_quote!(i8), 1),
345+
(syn::parse_quote!(u16), 2),
346+
(syn::parse_quote!(i16), 2),
347+
(syn::parse_quote!(u32), 4),
348+
(syn::parse_quote!(i32), 4),
349+
(syn::parse_quote!(f32), 4),
350+
(syn::parse_quote!(f64), 8),
351+
];
352+
if let Some((_ty, size)) = valid_types.iter().find(|x| x.0 == *ty) {
353+
Ok(Some(*size))
354+
} else {
355+
Err(Error::new(
356+
ty.span(),
357+
format!("Not supported type, expect one of {:?}", valid_types),
358+
))
359+
}
360+
}

0 commit comments

Comments
 (0)