@@ -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,138 @@ 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+
230287 let variants = variants. iter ( ) . enumerate ( ) . map ( |( i, v) | {
231288 let variant = & v. name ;
232289
233- if v. attrs . allow_unset {
234- quote ! {
235- #i => {
236- let individual = match event. data {
237- Value :: Individual ( individual) => individual,
238- Value :: Unset => {
239- return Ok ( #name:: #variant( None ) ) ;
240- } ,
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)
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" ) ,
252348 }
253349 }
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+
370+ let variants = variants. iter ( ) . enumerate ( ) . map ( |( i, v) | {
371+ let variant = & v. name ;
372+
373+ let prop_set_case = if v. attrs . allow_unset {
374+ quote ! { Some ( value) }
375+ } else {
376+ quote ! { value }
377+ } ;
378+
379+ let prop_unset = if v. attrs . allow_unset {
380+ quote ! { Ok ( #name:: #variant( None ) ) }
254381 } else {
255382 quote ! {
256- #i => {
257- let individual = match event. data {
258- Value :: Individual ( individual) => individual,
259- Value :: Unset => {
260- return Err ( FromEventError :: Unset {
261- interface: INTERFACE ,
262- endpoint: event. path,
263- } ) ;
264- } ,
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)
383+ return Err ( FromEventError :: Unset {
384+ interface: INTERFACE ,
385+ endpoint: event. path,
386+ } ) ;
387+ }
388+ } ;
389+
390+ quote ! {
391+ #i => {
392+ match event. data {
393+ Value :: Individual { ..} | Value :: Object { ..} => {
394+ return Err ( FromEventError :: InterfaceType ( InterfaceTypeError :: with_path(
395+ event. interface,
396+ event. path,
397+ InterfaceTypeDef :: Properties ,
398+ InterfaceTypeDef :: Datastream ,
399+ ) ) ) ;
400+ } ,
401+ Value :: Property ( Some ( prop) ) => {
402+ prop. try_into( )
403+ . map( |value| #name:: #variant( #prop_set_case) )
404+ . map_err( FromEventError :: from)
405+ } ,
406+ Value :: Property ( None ) => {
407+ #prop_unset
408+ } ,
274409 }
275410 }
276411 }
@@ -283,6 +418,8 @@ impl FromEventDerive {
283418 fn from_event( event: astarte_device_sdk:: DeviceEvent ) -> :: std:: result:: Result <Self , Self :: Err > {
284419 use astarte_device_sdk:: Value ;
285420 use astarte_device_sdk:: AstarteType ;
421+ use astarte_device_sdk:: interface:: def:: { Aggregation , InterfaceTypeDef } ;
422+ use astarte_device_sdk:: error:: { AggregationError , InterfaceTypeError } ;
286423 use astarte_device_sdk:: event:: FromEventError ;
287424 use astarte_device_sdk:: interface:: mapping:: endpoint:: Endpoint ;
288425
@@ -337,13 +474,15 @@ impl Parse for FromEventDerive {
337474 )
338475 } ) ?;
339476
340- let aggregation = match attrs. aggregation . unwrap_or_default ( ) {
341- Aggregation :: Individual => {
477+ let aggregation = attrs. aggregation . unwrap_or_default ( ) ;
478+ let interface_type = attrs. interface_type . unwrap_or_default ( ) ;
479+ let inner = match ( aggregation, interface_type) {
480+ ( Aggregation :: Individual , InterfaceType :: Datastream ) => {
342481 let variants = FromEventAggregation :: parse_enum_variants ( & ast) ?;
343482
344483 FromEventAggregation :: Individual { variants }
345484 }
346- Aggregation :: Object => {
485+ ( Aggregation :: Object , InterfaceType :: Datastream ) => {
347486 let path = attrs. path . ok_or_else ( || {
348487 syn:: Error :: new (
349488 ast. span ( ) ,
@@ -355,6 +494,17 @@ impl Parse for FromEventDerive {
355494
356495 FromEventAggregation :: Object { fields, path }
357496 }
497+ ( Aggregation :: Individual , InterfaceType :: Properties ) => {
498+ let variants = FromEventAggregation :: parse_enum_variants ( & ast) ?;
499+
500+ FromEventAggregation :: Property { variants }
501+ }
502+ ( Aggregation :: Object , InterfaceType :: Properties ) => {
503+ return Err ( syn:: Error :: new (
504+ ast. span ( ) ,
505+ "object properties are not supported" ,
506+ ) ) ;
507+ }
358508 } ;
359509
360510 let generics = Self :: add_trait_bound ( ast. generics ) ;
@@ -364,14 +514,15 @@ impl Parse for FromEventDerive {
364514 rename_rule : attrs. rename_rule ,
365515 name : ast. ident ,
366516 generics,
367- aggregation ,
517+ inner ,
368518 } )
369519 }
370520}
371521
372522enum FromEventAggregation {
373523 Individual { variants : Vec < IndividualMapping > } ,
374524 Object { fields : Vec < Ident > , path : String } ,
525+ Property { variants : Vec < IndividualMapping > } ,
375526}
376527
377528impl FromEventAggregation {
@@ -443,7 +594,7 @@ struct MappingAttr {
443594 endpoint : String ,
444595 /// Allow [`Option`]al values for properties.
445596 ///
446- /// Defaults to false as in the interfaces definition.
597+ /// Defaults to false as in the interfaces definition. Only available with `interface_type = "properties"`
447598 allow_unset : bool ,
448599}
449600
0 commit comments