1use proc_macro::TokenStream;
2
3use quote::quote;
4use syn::parse::{Parse, ParseStream};
5use syn::punctuated::Punctuated;
6use syn::{parse_macro_input, DeriveInput, Result, Token};
7use syn::{Expr, ExprLit, Lit, MetaNameValue};
8
9struct Attributes {
11 base_offset: usize,
12 report_id: u8,
13 cmd_len: usize,
14}
15
16impl Parse for Attributes {
17 fn parse(input: ParseStream) -> Result<Self> {
18 let args = Punctuated::<MetaNameValue, Token![,]>::parse_terminated(input)?;
20 let mut base_offset_opt = None;
21 let mut report_id_opt = None;
22 let mut cmd_len_opt = None;
23
24 for arg in args {
25 let key = arg
27 .path
28 .get_ident()
29 .ok_or_else(|| syn::Error::new_spanned(&arg.path, "Expected identifier"))?
30 .to_string();
31
32 let lit_int = if let Expr::Lit(ExprLit {
34 lit: Lit::Int(ref i),
35 ..
36 }) = arg.value
37 {
38 i
39 } else {
40 return Err(syn::Error::new_spanned(
41 &arg.value,
42 "Expected integer literal",
43 ));
44 };
45
46 match key.as_str() {
47 "base_offset" => {
48 base_offset_opt = Some(lit_int.base10_parse()?);
49 }
50 "report_id" => {
51 report_id_opt = Some(lit_int.base10_parse()?);
52 }
53 "cmd_len" => {
54 cmd_len_opt = Some(lit_int.base10_parse()?);
55 }
56 _ => return Err(syn::Error::new_spanned(arg, "Unknown attribute key")),
57 }
58 }
59
60 let base_offset = base_offset_opt
62 .ok_or_else(|| syn::Error::new(input.span(), "Missing `base_offset`"))?;
63 let report_id =
64 report_id_opt.ok_or_else(|| syn::Error::new(input.span(), "Missing `report_id`"))?;
65 let cmd_len =
66 cmd_len_opt.ok_or_else(|| syn::Error::new(input.span(), "Missing `cmd_len`"))?;
67
68 Ok(Attributes {
69 base_offset,
70 report_id,
71 cmd_len,
72 })
73 }
74}
75
76#[proc_macro_derive(CommandDescriptor, attributes(command_descriptor))]
77pub fn derive_my_trait(input: TokenStream) -> TokenStream {
78 let ast = parse_macro_input!(input as DeriveInput);
79
80 let mut args_opt = None;
81 for attr in ast.attrs.iter() {
82 if attr.path().is_ident("command_descriptor") {
83 let args: Attributes = attr
85 .parse_args()
86 .expect("Failed to parse command_descriptor arguments");
87 args_opt = Some(args);
88 break;
89 }
90 }
91
92 let args = args_opt.expect("Missing #[command_descriptor(...)] attribute");
93 let base_offset = args.base_offset;
94 let report_id = args.report_id;
95 let cmd_len = args.cmd_len;
96
97 let name = &ast.ident;
98
99 let gen = quote! {
100 impl CommandDescriptor for #name {
101 fn base_offset() -> usize {
102 #base_offset
103 }
104
105 fn report_id() -> u8 {
106 #report_id
107 }
108
109 fn cmd_len() -> usize {
110 #cmd_len
111 }
112 }
113 };
114
115 TokenStream::from(gen)
116}