astarte_device_sdk_derive/
lib.rs1use std::fmt::Debug;
22
23use darling::{FromDeriveInput, FromField};
24use proc_macro::TokenStream;
25
26use quote::{quote, quote_spanned};
27use syn::{GenericParam, Generics, parse_macro_input, parse_quote};
28
29use crate::{case::RenameRule, event::FromEventDerive};
30
31mod case;
32mod event;
33
34#[derive(Debug, FromDeriveInput)]
49#[darling(attributes(astarte_object), supports(struct_named))]
50struct ObjectDerive {
51 #[darling(default)]
53 rename_all: Option<RenameRule>,
54 ident: syn::Ident,
56 generics: syn::Generics,
58 data: darling::ast::Data<(), ObjectField>,
60}
61
62impl ObjectDerive {
63 fn quote(&self) -> darling::Result<proc_macro2::TokenStream> {
64 let st_name = &self.ident;
65
66 let Some(fields) = self.data.as_ref().take_struct() else {
67 return Err(darling::Error::unsupported_shape_with_expected(
68 "enum",
69 &"object must be astruct",
70 )
71 .with_span(&self.ident));
72 };
73
74 let mut errors = darling::Error::accumulator();
75
76 let capacity = fields.len();
77 let fields = fields.iter()
78 .filter_map(|field| {
79 errors.handle_in(||
80 field.field_name(self.rename_all).ok_or_else(||
81 darling::Error::custom( "missing struct fields")
82 .with_span(&self.ident)
83 ))
84 }).map(|(field_i, field_n)| {
85 quote_spanned! {field_i.span() =>
86 #[allow(unknown_lints)]
88 #[allow(clippy::unnecessary_fallible_conversions)]
89 let v: astarte_device_sdk::types::AstarteData = ::std::convert::TryInto::try_into(value.#field_i)?;
90 object.insert(#field_n.to_string(), v);
91 }
92 }).collect::<Vec<_>>();
93
94 errors.finish()?;
95
96 let generics = Self::add_trait_bound(&self.generics);
97 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
98
99 Ok(quote! {
100 impl #impl_generics ::std::convert::TryFrom<#st_name #ty_generics> for astarte_device_sdk::aggregate::AstarteObject #where_clause {
101 type Error = astarte_device_sdk::error::Error;
102
103 fn try_from(value: #st_name #ty_generics) -> ::std::result::Result<Self, Self::Error> {
104 let mut object = Self::with_capacity(#capacity);
105 #(#fields)*
106 Ok(object)
107 }
108 }
109 })
110 }
111
112 pub fn add_trait_bound(generics: &Generics) -> Generics {
113 let mut generics = generics.clone();
114
115 for param in &mut generics.params {
116 if let GenericParam::Type(type_param) = param {
117 type_param.bounds.push(parse_quote!(
118 std::convert::TryInto<astarte_device_sdk::types::AstarteData, Error = astarte_device_sdk::error::Error>
119 ));
120 }
121 }
122
123 generics
124 }
125}
126
127#[derive(Debug, FromField)]
128#[darling(attributes(astarte_object))]
129struct ObjectField {
130 #[darling(default)]
132 rename: Option<String>,
133 ident: Option<syn::Ident>,
135}
136
137impl ObjectField {
138 fn field_name(&self, rename_rule: Option<RenameRule>) -> Option<(&syn::Ident, String)> {
139 self.ident.as_ref().map(|i| {
140 let mut name = i.to_string();
141
142 if let Some(rename) = &self.rename {
143 name = rename.clone();
144 } else if let Some(rename_rule) = rename_rule {
145 name = rename_rule.apply_to_field(&name);
146 }
147
148 (i, name)
149 })
150 }
151}
152
153#[proc_macro_derive(IntoAstarteObject, attributes(astarte_object))]
164pub fn astarte_aggregate_derive(input: TokenStream) -> TokenStream {
165 let input = parse_macro_input!(input as syn::DeriveInput);
166
167 match ObjectDerive::from_derive_input(&input).and_then(|obj| obj.quote()) {
168 Ok(t) => t.into(),
169 Err(error) => error.write_errors().into(),
170 }
171}
172
173#[proc_macro_derive(FromEvent, attributes(from_event, mapping))]
214pub fn from_event_derive(input: TokenStream) -> TokenStream {
215 let input = parse_macro_input!(input as syn::DeriveInput);
216
217 let res = FromEventDerive::from_derive_input(&input).and_then(|from_event| from_event.quote());
218
219 match res {
221 Ok(t) => t.into(),
222 Err(err) => err.write_errors().into(),
223 }
224}