Skip to content

Commit d56a954

Browse files
committed
refactor!(value): add property and timestamp to the Value enum
Signed-off-by: Joshua Chapman <joshua.chapman@secomind.com>
1 parent 03b739e commit d56a954

File tree

21 files changed

+816
-473
lines changed

21 files changed

+816
-473
lines changed

astarte-device-sdk-derive/src/event.rs

Lines changed: 202 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ pub(crate) struct FromEventAttrs {
4141
rename_rule: Option<RenameRule>,
4242
// Use an option, so it can be merged if declared multiple times.
4343
aggregation: Option<Aggregation>,
44+
interface_type: Option<InterfaceType>,
4445
}
4546

4647
impl FromEventAttrs {
@@ -49,12 +50,14 @@ impl FromEventAttrs {
4950
let path = other.path.or(self.path);
5051
let rename_rule = other.rename_rule.or(self.rename_rule);
5152
let aggregation = other.aggregation.or(self.aggregation);
53+
let interface_type = other.interface_type.or(self.interface_type);
5254

5355
Self {
5456
interface,
5557
path,
5658
rename_rule,
5759
aggregation,
60+
interface_type,
5861
}
5962
}
6063
}
@@ -88,6 +91,11 @@ impl Parse for FromEventAttrs {
8891
.map(Aggregation::try_from)
8992
.transpose()?;
9093

94+
let interface_type = attrs
95+
.remove("interface_type")
96+
.map(InterfaceType::try_from)
97+
.transpose()?;
98+
9199
if let Some((_, expr)) = attrs.iter().next() {
92100
return Err(syn::Error::new(expr.span(), "unrecognized attribute"));
93101
}
@@ -97,14 +105,15 @@ impl Parse for FromEventAttrs {
97105
interface,
98106
path,
99107
aggregation,
108+
interface_type,
100109
})
101110
}
102111
}
103112

104113
#[derive(Debug, Default)]
105114
enum Aggregation {
106-
Individual,
107115
#[default]
116+
Individual,
108117
Object,
109118
}
110119

@@ -123,13 +132,35 @@ impl TryFrom<Expr> for Aggregation {
123132
}
124133
}
125134

135+
#[derive(Debug, Default)]
136+
enum InterfaceType {
137+
#[default]
138+
Datastream,
139+
Properties,
140+
}
141+
142+
impl TryFrom<Expr> for InterfaceType {
143+
type Error = syn::Error;
144+
145+
fn try_from(value: Expr) -> Result<Self, Self::Error> {
146+
parse_str_lit(&value).and_then(|val| match val.as_str() {
147+
"properties" => Ok(InterfaceType::Properties),
148+
"object" => Ok(InterfaceType::Datastream),
149+
_ => Err(syn::Error::new(
150+
value.span(),
151+
"invalid interface type, should be: property or datastream",
152+
)),
153+
})
154+
}
155+
}
156+
126157
/// Parses the derive for the FromEvent trait
127158
pub(crate) struct FromEventDerive {
128159
interface: String,
129160
name: Ident,
130161
rename_rule: Option<RenameRule>,
131162
generics: Generics,
132-
aggregation: FromEventAggregation,
163+
inner: FromEventAggregation,
133164
}
134165

135166
impl FromEventDerive {
@@ -145,9 +176,10 @@ impl FromEventDerive {
145176
}
146177

147178
pub(crate) fn quote(&self) -> proc_macro2::TokenStream {
148-
match &self.aggregation {
179+
match &self.inner {
149180
FromEventAggregation::Individual { variants } => self.quote_indv(variants),
150181
FromEventAggregation::Object { fields, path } => self.quote_obj(path, fields),
182+
FromEventAggregation::Property { variants } => self.quote_property(variants),
151183
}
152184
}
153185

@@ -178,6 +210,8 @@ impl FromEventDerive {
178210

179211
fn from_event(event: astarte_device_sdk::DeviceEvent) -> ::std::result::Result<Self, Self::Err> {
180212
use astarte_device_sdk::Value;
213+
use astarte_device_sdk::interface::def::{Aggregation, InterfaceTypeDef};
214+
use astarte_device_sdk::error::{AggregationError, InterfaceTypeError};
181215
use astarte_device_sdk::event::FromEventError;
182216
use astarte_device_sdk::interface::mapping::endpoint::Endpoint;
183217

@@ -196,11 +230,24 @@ impl FromEventDerive {
196230
});
197231
}
198232

199-
let Value::Object(mut object) = event.data else {
200-
return Err(FromEventError::Individual {
201-
interface,
202-
base_path,
203-
});
233+
let mut object = match event.data {
234+
Value::Object{data, ..} => data,
235+
Value::Individual{..} => {
236+
return Err(FromEventError::Aggregation(AggregationError::new(
237+
interface,
238+
event.path,
239+
Aggregation::Object,
240+
Aggregation::Individual,
241+
)));
242+
},
243+
Value::Property(_) => {
244+
return Err(FromEventError::InterfaceType(InterfaceTypeError::with_path(
245+
interface,
246+
event.path,
247+
InterfaceTypeDef::Datastream,
248+
InterfaceTypeDef::Properties,
249+
)));
250+
},
204251
};
205252

206253
#(#fields_val)*
@@ -227,50 +274,149 @@ impl FromEventDerive {
227274
}
228275
});
229276

277+
for variant in variants {
278+
if variant.attrs.allow_unset {
279+
return syn::Error::new(
280+
variant.name.span(),
281+
r#"the attribute allow_unset is only usable with `interface_type = "property"` on the container"#,
282+
)
283+
.to_compile_error();
284+
}
285+
}
286+
287+
let variants = variants.iter().enumerate().map(|(i, v)| {
288+
let variant = &v.name;
289+
290+
quote! {
291+
#i => {
292+
let individual = match event.data {
293+
Value::Individual{data, ..} => data,
294+
Value::Object{..} => {
295+
return Err(FromEventError::Aggregation(AggregationError::new(
296+
event.interface,
297+
event.path,
298+
Aggregation::Individual,
299+
Aggregation::Object,
300+
)));
301+
},
302+
Value::Property(_) => {
303+
return Err(FromEventError::InterfaceType(InterfaceTypeError::with_path(
304+
event.interface,
305+
event.path,
306+
InterfaceTypeDef::Datastream,
307+
InterfaceTypeDef::Properties,
308+
)));
309+
},
310+
};
311+
312+
313+
individual.try_into().map(#name::#variant).map_err(FromEventError::from)
314+
}
315+
}
316+
});
317+
318+
quote! {
319+
impl #impl_generics astarte_device_sdk::FromEvent for #name #ty_generics #where_clause {
320+
type Err = astarte_device_sdk::event::FromEventError;
321+
322+
fn from_event(event: astarte_device_sdk::DeviceEvent) -> ::std::result::Result<Self, Self::Err> {
323+
use astarte_device_sdk::Value;
324+
use astarte_device_sdk::AstarteType;
325+
use astarte_device_sdk::interface::def::{Aggregation, InterfaceTypeDef};
326+
use astarte_device_sdk::error::{AggregationError, InterfaceTypeError};
327+
use astarte_device_sdk::event::FromEventError;
328+
use astarte_device_sdk::interface::mapping::endpoint::Endpoint;
329+
330+
const INTERFACE: &str = #interface;
331+
332+
if event.interface != INTERFACE {
333+
return Err(FromEventError::Interface(event.interface));
334+
}
335+
336+
let endpoints = [ #(#endpoints),* ];
337+
338+
let position = endpoints.iter()
339+
.position(|e| e.eq_mapping(&event.path))
340+
.ok_or_else(|| FromEventError::Path {
341+
interface: INTERFACE,
342+
base_path: event.path.clone(),
343+
})?;
344+
345+
match position {
346+
#(#variants)*
347+
_ => unreachable!("BUG: endpoint found, but outside the range of the variants"),
348+
}
349+
}
350+
}
351+
}
352+
}
353+
354+
fn quote_property(&self, variants: &[IndividualMapping]) -> proc_macro2::TokenStream {
355+
let (impl_generics, ty_generics, where_clause) = &self.generics.split_for_impl();
356+
357+
let name = &self.name;
358+
let interface = self.interface.as_str();
359+
360+
// Use the same order between endpoints and variants, so we can find the correct endpoint
361+
// position and then match the index with the corresponding variant.
362+
let endpoints = variants.iter().map(|v| {
363+
let endpoint = v.attrs.endpoint.as_str();
364+
365+
quote! {
366+
Endpoint::<&str>::try_from(#endpoint)?
367+
}
368+
});
369+
230370
let variants = variants.iter().enumerate().map(|(i, v)| {
231371
let variant = &v.name;
232372

233373
if v.attrs.allow_unset {
234374
quote! {
235375
#i => {
236-
let individual = match event.data {
237-
Value::Individual(individual) => individual,
238-
Value::Unset => {
239-
return Ok(#name::#variant(None));
376+
match event.data {
377+
Value::Individual{..} | Value::Object{..} => {
378+
return Err(FromEventError::InterfaceType(InterfaceTypeError::with_path(
379+
event.interface,
380+
event.path,
381+
InterfaceTypeDef::Properties,
382+
InterfaceTypeDef::Datastream,
383+
)));
240384
},
241-
Value::Object(_) => {
242-
return Err(FromEventError::Object {
243-
interface: INTERFACE,
244-
endpoint: event.path,
245-
});
246-
}
247-
};
248-
249-
individual.try_into()
250-
.map(|value| #name::#variant(Some(value)))
251-
.map_err(FromEventError::from)
385+
Value::Property(Some(prop)) => {
386+
prop.try_into()
387+
.map(|value| #name::#variant(Some(value)))
388+
.map_err(FromEventError::from)
389+
},
390+
Value::Property(None) => {
391+
Ok(#name::#variant(None))
392+
},
393+
}
252394
}
253395
}
254396
} else {
255397
quote! {
256398
#i => {
257-
let individual = match event.data {
258-
Value::Individual(individual) => individual,
259-
Value::Unset => {
399+
match event.data {
400+
Value::Individual{..} | Value::Object{..} => {
401+
return Err(FromEventError::InterfaceType(InterfaceTypeError::with_path(
402+
event.interface,
403+
event.path,
404+
InterfaceTypeDef::Properties,
405+
InterfaceTypeDef::Datastream,
406+
)));
407+
},
408+
Value::Property(Some(prop)) => {
409+
prop.try_into()
410+
.map(|value| #name::#variant(value))
411+
.map_err(FromEventError::from)
412+
},
413+
Value::Property(None) => {
260414
return Err(FromEventError::Unset {
261415
interface: INTERFACE,
262416
endpoint: event.path,
263417
});
264418
},
265-
Value::Object(_) => {
266-
return Err(FromEventError::Object {
267-
interface: INTERFACE,
268-
endpoint: event.path,
269-
});
270-
}
271-
};
272-
273-
individual.try_into().map(#name::#variant).map_err(FromEventError::from)
419+
}
274420
}
275421
}
276422
}
@@ -283,6 +429,8 @@ impl FromEventDerive {
283429
fn from_event(event: astarte_device_sdk::DeviceEvent) -> ::std::result::Result<Self, Self::Err> {
284430
use astarte_device_sdk::Value;
285431
use astarte_device_sdk::AstarteType;
432+
use astarte_device_sdk::interface::def::{Aggregation, InterfaceTypeDef};
433+
use astarte_device_sdk::error::{AggregationError, InterfaceTypeError};
286434
use astarte_device_sdk::event::FromEventError;
287435
use astarte_device_sdk::interface::mapping::endpoint::Endpoint;
288436

@@ -337,13 +485,15 @@ impl Parse for FromEventDerive {
337485
)
338486
})?;
339487

340-
let aggregation = match attrs.aggregation.unwrap_or_default() {
341-
Aggregation::Individual => {
488+
let aggregation = attrs.aggregation.unwrap_or_default();
489+
let interface_type = attrs.interface_type.unwrap_or_default();
490+
let inner = match (aggregation, interface_type) {
491+
(Aggregation::Individual, InterfaceType::Datastream) => {
342492
let variants = FromEventAggregation::parse_enum_variants(&ast)?;
343493

344494
FromEventAggregation::Individual { variants }
345495
}
346-
Aggregation::Object => {
496+
(Aggregation::Object, InterfaceType::Datastream) => {
347497
let path = attrs.path.ok_or_else(|| {
348498
syn::Error::new(
349499
ast.span(),
@@ -355,6 +505,17 @@ impl Parse for FromEventDerive {
355505

356506
FromEventAggregation::Object { fields, path }
357507
}
508+
(Aggregation::Individual, InterfaceType::Properties) => {
509+
let variants = FromEventAggregation::parse_enum_variants(&ast)?;
510+
511+
FromEventAggregation::Property { variants }
512+
}
513+
(Aggregation::Object, InterfaceType::Properties) => {
514+
return Err(syn::Error::new(
515+
ast.span(),
516+
"object properties are not supported",
517+
));
518+
}
358519
};
359520

360521
let generics = Self::add_trait_bound(ast.generics);
@@ -364,14 +525,15 @@ impl Parse for FromEventDerive {
364525
rename_rule: attrs.rename_rule,
365526
name: ast.ident,
366527
generics,
367-
aggregation,
528+
inner,
368529
})
369530
}
370531
}
371532

372533
enum FromEventAggregation {
373534
Individual { variants: Vec<IndividualMapping> },
374535
Object { fields: Vec<Ident>, path: String },
536+
Property { variants: Vec<IndividualMapping> },
375537
}
376538

377539
impl FromEventAggregation {
@@ -443,7 +605,7 @@ struct MappingAttr {
443605
endpoint: String,
444606
/// Allow [`Option`]al values for properties.
445607
///
446-
/// Defaults to false as in the interfaces definition.
608+
/// Defaults to false as in the interfaces definition. Only available with `interface_type = "properties"`
447609
allow_unset: bool,
448610
}
449611

0 commit comments

Comments
 (0)