@@ -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
4647impl 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 ) ]
105114enum 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
127158pub ( 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
135166impl 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
372533enum FromEventAggregation {
373534 Individual { variants : Vec < IndividualMapping > } ,
374535 Object { fields : Vec < Ident > , path : String } ,
536+ Property { variants : Vec < IndividualMapping > } ,
375537}
376538
377539impl 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