|
| 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