r2r_macros/
lib.rs
1use proc_macro2::TokenStream;
2use quote::{quote, quote_spanned};
3use syn::{parse_macro_input, spanned::Spanned, Data, DeriveInput, Fields};
4
5extern crate proc_macro;
6
7#[proc_macro_derive(RosParams)]
10pub fn derive_r2r_params(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
11 let input = parse_macro_input!(input as DeriveInput);
13
14 let name = input.ident;
16
17 let register_calls = get_register_calls(&input.data);
18 let get_param_matches = param_matches_for(quote!(get_parameter(suffix)), &input.data);
19 let set_param_matches =
20 param_matches_for(quote!(set_parameter(suffix, param_val)), &input.data);
21 let check_param_matches =
22 param_matches_for(quote!(check_parameter(suffix, param_val)), &input.data);
23
24 let expanded = quote! {
25 impl ::r2r::RosParams for #name {
27 fn register_parameters(
28 &mut self,
29 prefix: &str,
30 desc: ::std::option::Option<::r2r::Parameter>,
31 params: &mut ::r2r::indexmap::IndexMap<String, ::r2r::Parameter>,
32 ) -> ::r2r::Result<()> {
33 let prefix = if prefix.is_empty() {
34 String::from("")
35 } else {
36 format!("{prefix}.")
37 };
38 #register_calls
39 Ok(())
40 }
41 fn get_parameter(&mut self, param_name: &str) -> ::r2r::Result<::r2r::ParameterValue>
42 {
43 let (prefix, suffix) = match param_name.split_once('.') {
44 None => (param_name, ""),
45 Some((prefix, suffix)) => (prefix, suffix)
46 };
47 let result = match prefix {
48 #get_param_matches
49 _ => Err(::r2r::Error::InvalidParameterName {
50 name: "".into(),
51 }),
52 };
53 result.map_err(|e| e.update_param_name(¶m_name))
54 }
55 fn set_parameter(&mut self, param_name: &str, param_val: &::r2r::ParameterValue) -> ::r2r::Result<()>
56 {
57 let (prefix, suffix) = match param_name.split_once('.') {
58 None => (param_name, ""),
59 Some((prefix, suffix)) => (prefix, suffix)
60 };
61 let result = match prefix {
62 #set_param_matches
63 _ => Err(::r2r::Error::InvalidParameterName {
64 name: "".into(),
65 }),
66 };
67 result.map_err(|e| e.update_param_name(¶m_name))
68 }
69 fn check_parameter(&self, param_name: &str, param_val: &::r2r::ParameterValue) -> ::r2r::Result<()>
70 {
71 let (prefix, suffix) = match param_name.split_once('.') {
72 None => (param_name, ""),
73 Some((prefix, suffix)) => (prefix, suffix)
74 };
75 let result = match prefix {
76 #check_param_matches
77 _ => Err(::r2r::Error::InvalidParameterName {
78 name: "".into(),
79 }),
80 };
81 result.map_err(|e| e.update_param_name(¶m_name))
82 }
83 }
84 };
85
86 proc_macro::TokenStream::from(expanded)
88}
89
90fn get_register_calls(data: &Data) -> TokenStream {
92 match *data {
93 Data::Struct(ref data) => match data.fields {
94 Fields::Named(ref fields) => {
95 let field_matches = fields.named.iter().map(|f| {
96 let name = &f.ident;
97 let format_str = format!("{{prefix}}{}", name.as_ref().unwrap());
98 let desc = get_field_doc(f);
99 quote_spanned! {
100 f.span() =>
101 let param = ::r2r::Parameter {
102 value: ::r2r::ParameterValue::NotSet, description: #desc,
104 };
105 self.#name.register_parameters(&format!(#format_str), Some(param), params)?;
106 }
107 });
108 quote! {
109 #(#field_matches)*
110 }
111 }
112 _ => unimplemented!(),
113 },
114 Data::Enum(_) | Data::Union(_) => unimplemented!(),
115 }
116}
117
118fn get_field_doc(f: &syn::Field) -> String {
119 if let Some(doc) = f
120 .attrs
121 .iter()
122 .find(|&attr| attr.path().get_ident().is_some_and(|id| id == "doc"))
123 {
124 match &doc.meta.require_name_value().unwrap().value {
125 ::syn::Expr::Lit(exprlit) => match &exprlit.lit {
126 ::syn::Lit::Str(s) => s.value().trim().to_owned(),
127 _ => unimplemented!(),
128 },
129 _ => unimplemented!(),
130 }
131 } else {
132 "".to_string()
133 }
134}
135
136fn param_matches_for(call: TokenStream, data: &Data) -> TokenStream {
138 match *data {
139 Data::Struct(ref data) => match data.fields {
140 Fields::Named(ref fields) => {
141 let field_matches = fields.named.iter().map(|f| {
142 let name = &f.ident;
143 let name_str = format!("{}", name.as_ref().unwrap());
144 quote_spanned! {
145 f.span() =>
146 #name_str => {
147 self.#name.#call
148 }
149 }
150 });
151 quote! {
152 #(#field_matches)*
153 }
154 }
155 _ => unimplemented!(),
156 },
157 Data::Enum(_) | Data::Union(_) => unimplemented!(),
158 }
159}