1*9860b763SAndroid Build Coastguard Worker // Copyright 2022, The Android Open Source Project
2*9860b763SAndroid Build Coastguard Worker //
3*9860b763SAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*9860b763SAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*9860b763SAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*9860b763SAndroid Build Coastguard Worker //
7*9860b763SAndroid Build Coastguard Worker // http://www.apache.org/licenses/LICENSE-2.0
8*9860b763SAndroid Build Coastguard Worker //
9*9860b763SAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*9860b763SAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*9860b763SAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*9860b763SAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*9860b763SAndroid Build Coastguard Worker // limitations under the License.
14*9860b763SAndroid Build Coastguard Worker
15*9860b763SAndroid Build Coastguard Worker //! Derive macro for `AsCborValue`.
16*9860b763SAndroid Build Coastguard Worker use proc_macro2::TokenStream;
17*9860b763SAndroid Build Coastguard Worker use quote::{format_ident, quote, quote_spanned};
18*9860b763SAndroid Build Coastguard Worker use syn::{
19*9860b763SAndroid Build Coastguard Worker parse_macro_input, parse_quote, spanned::Spanned, Data, DeriveInput, Fields, GenericParam,
20*9860b763SAndroid Build Coastguard Worker Generics, Ident, Index,
21*9860b763SAndroid Build Coastguard Worker };
22*9860b763SAndroid Build Coastguard Worker
23*9860b763SAndroid Build Coastguard Worker /// Derive macro that implements the `AsCborValue` trait. Using this macro requires
24*9860b763SAndroid Build Coastguard Worker /// that `AsCborValue`, `CborError` and `cbor_type_error` are locally `use`d.
25*9860b763SAndroid Build Coastguard Worker #[proc_macro_derive(AsCborValue)]
derive_as_cbor_value(input: proc_macro::TokenStream) -> proc_macro::TokenStream26*9860b763SAndroid Build Coastguard Worker pub fn derive_as_cbor_value(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
27*9860b763SAndroid Build Coastguard Worker let input = parse_macro_input!(input as DeriveInput);
28*9860b763SAndroid Build Coastguard Worker derive_as_cbor_value_internal(&input)
29*9860b763SAndroid Build Coastguard Worker }
30*9860b763SAndroid Build Coastguard Worker
derive_as_cbor_value_internal(input: &DeriveInput) -> proc_macro::TokenStream31*9860b763SAndroid Build Coastguard Worker fn derive_as_cbor_value_internal(input: &DeriveInput) -> proc_macro::TokenStream {
32*9860b763SAndroid Build Coastguard Worker let name = &input.ident;
33*9860b763SAndroid Build Coastguard Worker
34*9860b763SAndroid Build Coastguard Worker // Add a bound `T: AsCborValue` for every type parameter `T`.
35*9860b763SAndroid Build Coastguard Worker let generics = add_trait_bounds(&input.generics);
36*9860b763SAndroid Build Coastguard Worker let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
37*9860b763SAndroid Build Coastguard Worker
38*9860b763SAndroid Build Coastguard Worker let from_val = from_val_struct(&input.data);
39*9860b763SAndroid Build Coastguard Worker let to_val = to_val_struct(&input.data);
40*9860b763SAndroid Build Coastguard Worker let cddl = cddl_struct(name, &input.data);
41*9860b763SAndroid Build Coastguard Worker
42*9860b763SAndroid Build Coastguard Worker let expanded = quote! {
43*9860b763SAndroid Build Coastguard Worker // The generated impl
44*9860b763SAndroid Build Coastguard Worker impl #impl_generics AsCborValue for #name #ty_generics #where_clause {
45*9860b763SAndroid Build Coastguard Worker fn from_cbor_value(value: ciborium::value::Value) -> Result<Self, CborError> {
46*9860b763SAndroid Build Coastguard Worker #from_val
47*9860b763SAndroid Build Coastguard Worker }
48*9860b763SAndroid Build Coastguard Worker fn to_cbor_value(self) -> Result<ciborium::value::Value, CborError> {
49*9860b763SAndroid Build Coastguard Worker #to_val
50*9860b763SAndroid Build Coastguard Worker }
51*9860b763SAndroid Build Coastguard Worker fn cddl_typename() -> Option<String> {
52*9860b763SAndroid Build Coastguard Worker Some(stringify!(#name).to_string())
53*9860b763SAndroid Build Coastguard Worker }
54*9860b763SAndroid Build Coastguard Worker fn cddl_schema() -> Option<String> {
55*9860b763SAndroid Build Coastguard Worker #cddl
56*9860b763SAndroid Build Coastguard Worker }
57*9860b763SAndroid Build Coastguard Worker }
58*9860b763SAndroid Build Coastguard Worker };
59*9860b763SAndroid Build Coastguard Worker
60*9860b763SAndroid Build Coastguard Worker expanded.into()
61*9860b763SAndroid Build Coastguard Worker }
62*9860b763SAndroid Build Coastguard Worker
63*9860b763SAndroid Build Coastguard Worker /// Add a bound `T: AsCborValue` for every type parameter `T`.
add_trait_bounds(generics: &Generics) -> Generics64*9860b763SAndroid Build Coastguard Worker fn add_trait_bounds(generics: &Generics) -> Generics {
65*9860b763SAndroid Build Coastguard Worker let mut generics = generics.clone();
66*9860b763SAndroid Build Coastguard Worker for param in &mut generics.params {
67*9860b763SAndroid Build Coastguard Worker if let GenericParam::Type(ref mut type_param) = *param {
68*9860b763SAndroid Build Coastguard Worker type_param.bounds.push(parse_quote!(AsCborValue));
69*9860b763SAndroid Build Coastguard Worker }
70*9860b763SAndroid Build Coastguard Worker }
71*9860b763SAndroid Build Coastguard Worker generics
72*9860b763SAndroid Build Coastguard Worker }
73*9860b763SAndroid Build Coastguard Worker
74*9860b763SAndroid Build Coastguard Worker /// Generate an expression to convert an instance of a compound type to `ciborium::value::Value`
to_val_struct(data: &Data) -> TokenStream75*9860b763SAndroid Build Coastguard Worker fn to_val_struct(data: &Data) -> TokenStream {
76*9860b763SAndroid Build Coastguard Worker match *data {
77*9860b763SAndroid Build Coastguard Worker Data::Struct(ref data) => {
78*9860b763SAndroid Build Coastguard Worker match data.fields {
79*9860b763SAndroid Build Coastguard Worker Fields::Named(ref fields) => {
80*9860b763SAndroid Build Coastguard Worker // Expands to an expression like
81*9860b763SAndroid Build Coastguard Worker //
82*9860b763SAndroid Build Coastguard Worker // {
83*9860b763SAndroid Build Coastguard Worker // let mut v = Vec::new();
84*9860b763SAndroid Build Coastguard Worker // v.try_reserve(3).map_err(|_e| CborError::AllocationFailed)?;
85*9860b763SAndroid Build Coastguard Worker // v.push(AsCborValue::to_cbor_value(self.x)?);
86*9860b763SAndroid Build Coastguard Worker // v.push(AsCborValue::to_cbor_value(self.y)?);
87*9860b763SAndroid Build Coastguard Worker // v.push(AsCborValue::to_cbor_value(self.z)?);
88*9860b763SAndroid Build Coastguard Worker // Ok(ciborium::value::Value::Array(v))
89*9860b763SAndroid Build Coastguard Worker // }
90*9860b763SAndroid Build Coastguard Worker let nfields = fields.named.len();
91*9860b763SAndroid Build Coastguard Worker let recurse = fields.named.iter().map(|f| {
92*9860b763SAndroid Build Coastguard Worker let name = &f.ident;
93*9860b763SAndroid Build Coastguard Worker quote_spanned! {f.span()=>
94*9860b763SAndroid Build Coastguard Worker v.push(AsCborValue::to_cbor_value(self.#name)?)
95*9860b763SAndroid Build Coastguard Worker }
96*9860b763SAndroid Build Coastguard Worker });
97*9860b763SAndroid Build Coastguard Worker quote! {
98*9860b763SAndroid Build Coastguard Worker {
99*9860b763SAndroid Build Coastguard Worker let mut v = Vec::new();
100*9860b763SAndroid Build Coastguard Worker v.try_reserve(#nfields).map_err(|_e| CborError::AllocationFailed)?;
101*9860b763SAndroid Build Coastguard Worker #(#recurse; )*
102*9860b763SAndroid Build Coastguard Worker Ok(ciborium::value::Value::Array(v))
103*9860b763SAndroid Build Coastguard Worker }
104*9860b763SAndroid Build Coastguard Worker }
105*9860b763SAndroid Build Coastguard Worker }
106*9860b763SAndroid Build Coastguard Worker Fields::Unnamed(ref fields) if fields.unnamed.len() == 1 => {
107*9860b763SAndroid Build Coastguard Worker // For a newtype, expands to an expression
108*9860b763SAndroid Build Coastguard Worker //
109*9860b763SAndroid Build Coastguard Worker // self.0.to_cbor_value()
110*9860b763SAndroid Build Coastguard Worker quote! {
111*9860b763SAndroid Build Coastguard Worker self.0.to_cbor_value()
112*9860b763SAndroid Build Coastguard Worker }
113*9860b763SAndroid Build Coastguard Worker }
114*9860b763SAndroid Build Coastguard Worker Fields::Unnamed(ref fields) => {
115*9860b763SAndroid Build Coastguard Worker // Expands to an expression like
116*9860b763SAndroid Build Coastguard Worker //
117*9860b763SAndroid Build Coastguard Worker //
118*9860b763SAndroid Build Coastguard Worker // {
119*9860b763SAndroid Build Coastguard Worker // let mut v = Vec::new();
120*9860b763SAndroid Build Coastguard Worker // v.try_reserve(3).map_err(|_e| CborError::AllocationFailed)?;
121*9860b763SAndroid Build Coastguard Worker // v.push(AsCborValue::to_cbor_value(self.0)?);
122*9860b763SAndroid Build Coastguard Worker // v.push(AsCborValue::to_cbor_value(self.1)?);
123*9860b763SAndroid Build Coastguard Worker // v.push(AsCborValue::to_cbor_value(self.2)?);
124*9860b763SAndroid Build Coastguard Worker // Ok(ciborium::value::Value::Array(v))
125*9860b763SAndroid Build Coastguard Worker // }
126*9860b763SAndroid Build Coastguard Worker let nfields = fields.unnamed.len();
127*9860b763SAndroid Build Coastguard Worker let recurse = fields.unnamed.iter().enumerate().map(|(i, f)| {
128*9860b763SAndroid Build Coastguard Worker let index = Index::from(i);
129*9860b763SAndroid Build Coastguard Worker quote_spanned! {f.span()=>
130*9860b763SAndroid Build Coastguard Worker v.push(AsCborValue::to_cbor_value(self.#index)?)
131*9860b763SAndroid Build Coastguard Worker }
132*9860b763SAndroid Build Coastguard Worker });
133*9860b763SAndroid Build Coastguard Worker quote! {
134*9860b763SAndroid Build Coastguard Worker {
135*9860b763SAndroid Build Coastguard Worker let mut v = Vec::new();
136*9860b763SAndroid Build Coastguard Worker v.try_reserve(#nfields).map_err(|_e| CborError::AllocationFailed)?;
137*9860b763SAndroid Build Coastguard Worker #(#recurse; )*
138*9860b763SAndroid Build Coastguard Worker Ok(ciborium::value::Value::Array(v))
139*9860b763SAndroid Build Coastguard Worker }
140*9860b763SAndroid Build Coastguard Worker }
141*9860b763SAndroid Build Coastguard Worker }
142*9860b763SAndroid Build Coastguard Worker Fields::Unit => unimplemented!(),
143*9860b763SAndroid Build Coastguard Worker }
144*9860b763SAndroid Build Coastguard Worker }
145*9860b763SAndroid Build Coastguard Worker Data::Enum(_) => {
146*9860b763SAndroid Build Coastguard Worker quote! {
147*9860b763SAndroid Build Coastguard Worker let v: ciborium::value::Integer = (self as i32).into();
148*9860b763SAndroid Build Coastguard Worker Ok(ciborium::value::Value::Integer(v))
149*9860b763SAndroid Build Coastguard Worker }
150*9860b763SAndroid Build Coastguard Worker }
151*9860b763SAndroid Build Coastguard Worker Data::Union(_) => unimplemented!(),
152*9860b763SAndroid Build Coastguard Worker }
153*9860b763SAndroid Build Coastguard Worker }
154*9860b763SAndroid Build Coastguard Worker
155*9860b763SAndroid Build Coastguard Worker /// Generate an expression to convert a `ciborium::value::Value` into an instance of a compound
156*9860b763SAndroid Build Coastguard Worker /// type.
from_val_struct(data: &Data) -> TokenStream157*9860b763SAndroid Build Coastguard Worker fn from_val_struct(data: &Data) -> TokenStream {
158*9860b763SAndroid Build Coastguard Worker match data {
159*9860b763SAndroid Build Coastguard Worker Data::Struct(ref data) => {
160*9860b763SAndroid Build Coastguard Worker match data.fields {
161*9860b763SAndroid Build Coastguard Worker Fields::Named(ref fields) => {
162*9860b763SAndroid Build Coastguard Worker // Expands to an expression like
163*9860b763SAndroid Build Coastguard Worker //
164*9860b763SAndroid Build Coastguard Worker // let mut a = match value {
165*9860b763SAndroid Build Coastguard Worker // ciborium::value::Value::Array(a) => a,
166*9860b763SAndroid Build Coastguard Worker // _ => return cbor_type_error(&value, "arr"),
167*9860b763SAndroid Build Coastguard Worker // };
168*9860b763SAndroid Build Coastguard Worker // if a.len() != 3 {
169*9860b763SAndroid Build Coastguard Worker // return Err(CborError::UnexpectedItem("arr", "arr len 3"));
170*9860b763SAndroid Build Coastguard Worker // }
171*9860b763SAndroid Build Coastguard Worker // // Fields specified in reverse order to reduce shifting.
172*9860b763SAndroid Build Coastguard Worker // Ok(Self {
173*9860b763SAndroid Build Coastguard Worker // z: <ZType>::from_cbor_value(a.remove(2))?,
174*9860b763SAndroid Build Coastguard Worker // y: <YType>::from_cbor_value(a.remove(1))?,
175*9860b763SAndroid Build Coastguard Worker // x: <XType>::from_cbor_value(a.remove(0))?,
176*9860b763SAndroid Build Coastguard Worker // })
177*9860b763SAndroid Build Coastguard Worker //
178*9860b763SAndroid Build Coastguard Worker // but using fully qualified function call syntax.
179*9860b763SAndroid Build Coastguard Worker let nfields = fields.named.len();
180*9860b763SAndroid Build Coastguard Worker let recurse = fields.named.iter().enumerate().rev().map(|(i, f)| {
181*9860b763SAndroid Build Coastguard Worker let name = &f.ident;
182*9860b763SAndroid Build Coastguard Worker let index = Index::from(i);
183*9860b763SAndroid Build Coastguard Worker let typ = &f.ty;
184*9860b763SAndroid Build Coastguard Worker quote_spanned! {f.span()=>
185*9860b763SAndroid Build Coastguard Worker #name: <#typ>::from_cbor_value(a.remove(#index))?
186*9860b763SAndroid Build Coastguard Worker }
187*9860b763SAndroid Build Coastguard Worker });
188*9860b763SAndroid Build Coastguard Worker quote! {
189*9860b763SAndroid Build Coastguard Worker let mut a = match value {
190*9860b763SAndroid Build Coastguard Worker ciborium::value::Value::Array(a) => a,
191*9860b763SAndroid Build Coastguard Worker _ => return cbor_type_error(&value, "arr"),
192*9860b763SAndroid Build Coastguard Worker };
193*9860b763SAndroid Build Coastguard Worker if a.len() != #nfields {
194*9860b763SAndroid Build Coastguard Worker return Err(CborError::UnexpectedItem(
195*9860b763SAndroid Build Coastguard Worker "arr",
196*9860b763SAndroid Build Coastguard Worker concat!("arr len ", stringify!(#nfields)),
197*9860b763SAndroid Build Coastguard Worker ));
198*9860b763SAndroid Build Coastguard Worker }
199*9860b763SAndroid Build Coastguard Worker // Fields specified in reverse order to reduce shifting.
200*9860b763SAndroid Build Coastguard Worker Ok(Self {
201*9860b763SAndroid Build Coastguard Worker #(#recurse, )*
202*9860b763SAndroid Build Coastguard Worker })
203*9860b763SAndroid Build Coastguard Worker }
204*9860b763SAndroid Build Coastguard Worker }
205*9860b763SAndroid Build Coastguard Worker Fields::Unnamed(ref fields) if fields.unnamed.len() == 1 => {
206*9860b763SAndroid Build Coastguard Worker // For a newtype, expands to an expression like
207*9860b763SAndroid Build Coastguard Worker //
208*9860b763SAndroid Build Coastguard Worker // Ok(Self(<InnerType>::from_cbor_value(value)?))
209*9860b763SAndroid Build Coastguard Worker let inner = fields.unnamed.first().unwrap();
210*9860b763SAndroid Build Coastguard Worker let typ = &inner.ty;
211*9860b763SAndroid Build Coastguard Worker quote! {
212*9860b763SAndroid Build Coastguard Worker Ok(Self(<#typ>::from_cbor_value(value)?))
213*9860b763SAndroid Build Coastguard Worker }
214*9860b763SAndroid Build Coastguard Worker }
215*9860b763SAndroid Build Coastguard Worker Fields::Unnamed(ref fields) => {
216*9860b763SAndroid Build Coastguard Worker // Expands to an expression like
217*9860b763SAndroid Build Coastguard Worker //
218*9860b763SAndroid Build Coastguard Worker // let mut a = match value {
219*9860b763SAndroid Build Coastguard Worker // ciborium::value::Value::Array(a) => a,
220*9860b763SAndroid Build Coastguard Worker // _ => return cbor_type_error(&value, "arr"),
221*9860b763SAndroid Build Coastguard Worker // };
222*9860b763SAndroid Build Coastguard Worker // if a.len() != 3 {
223*9860b763SAndroid Build Coastguard Worker // return Err(CborError::UnexpectedItem("arr", "arr len 3"));
224*9860b763SAndroid Build Coastguard Worker // }
225*9860b763SAndroid Build Coastguard Worker // // Fields specified in reverse order to reduce shifting.
226*9860b763SAndroid Build Coastguard Worker // let field_2 = <Type2>::from_cbor_value(a.remove(2))?;
227*9860b763SAndroid Build Coastguard Worker // let field_1 = <Type1>::from_cbor_value(a.remove(1))?;
228*9860b763SAndroid Build Coastguard Worker // let field_0 = <Type0>::from_cbor_value(a.remove(0))?;
229*9860b763SAndroid Build Coastguard Worker // Ok(Self(field_0, field_1, field_2))
230*9860b763SAndroid Build Coastguard Worker let nfields = fields.unnamed.len();
231*9860b763SAndroid Build Coastguard Worker let recurse1 = fields.unnamed.iter().enumerate().rev().map(|(i, f)| {
232*9860b763SAndroid Build Coastguard Worker let typ = &f.ty;
233*9860b763SAndroid Build Coastguard Worker let varname = format_ident!("field_{}", i);
234*9860b763SAndroid Build Coastguard Worker quote_spanned! {f.span()=>
235*9860b763SAndroid Build Coastguard Worker let #varname = <#typ>::from_cbor_value(a.remove(#i))?;
236*9860b763SAndroid Build Coastguard Worker }
237*9860b763SAndroid Build Coastguard Worker });
238*9860b763SAndroid Build Coastguard Worker let recurse2 = fields.unnamed.iter().enumerate().map(|(i, f)| {
239*9860b763SAndroid Build Coastguard Worker let varname = format_ident!("field_{}", i);
240*9860b763SAndroid Build Coastguard Worker quote_spanned! {f.span()=>
241*9860b763SAndroid Build Coastguard Worker #varname
242*9860b763SAndroid Build Coastguard Worker }
243*9860b763SAndroid Build Coastguard Worker });
244*9860b763SAndroid Build Coastguard Worker quote! {
245*9860b763SAndroid Build Coastguard Worker let mut a = match value {
246*9860b763SAndroid Build Coastguard Worker ciborium::value::Value::Array(a) => a,
247*9860b763SAndroid Build Coastguard Worker _ => return cbor_type_error(&value, "arr"),
248*9860b763SAndroid Build Coastguard Worker };
249*9860b763SAndroid Build Coastguard Worker if a.len() != #nfields {
250*9860b763SAndroid Build Coastguard Worker return Err(CborError::UnexpectedItem("arr",
251*9860b763SAndroid Build Coastguard Worker concat!("arr len ",
252*9860b763SAndroid Build Coastguard Worker stringify!(#nfields))));
253*9860b763SAndroid Build Coastguard Worker }
254*9860b763SAndroid Build Coastguard Worker // Fields specified in reverse order to reduce shifting.
255*9860b763SAndroid Build Coastguard Worker #(#recurse1)*
256*9860b763SAndroid Build Coastguard Worker
257*9860b763SAndroid Build Coastguard Worker Ok(Self( #(#recurse2, )* ))
258*9860b763SAndroid Build Coastguard Worker }
259*9860b763SAndroid Build Coastguard Worker }
260*9860b763SAndroid Build Coastguard Worker Fields::Unit => unimplemented!(),
261*9860b763SAndroid Build Coastguard Worker }
262*9860b763SAndroid Build Coastguard Worker }
263*9860b763SAndroid Build Coastguard Worker Data::Enum(enum_data) => {
264*9860b763SAndroid Build Coastguard Worker // This only copes with variants with no fields.
265*9860b763SAndroid Build Coastguard Worker // Expands to an expression like:
266*9860b763SAndroid Build Coastguard Worker //
267*9860b763SAndroid Build Coastguard Worker // use core::convert::TryInto;
268*9860b763SAndroid Build Coastguard Worker // let v: i32 = match value {
269*9860b763SAndroid Build Coastguard Worker // ciborium::value::Value::Integer(i) => i.try_into().map_err(|_| {
270*9860b763SAndroid Build Coastguard Worker // CborError::OutOfRangeIntegerValue
271*9860b763SAndroid Build Coastguard Worker // })?,
272*9860b763SAndroid Build Coastguard Worker // v => return cbor_type_error(&v, &"int"),
273*9860b763SAndroid Build Coastguard Worker // };
274*9860b763SAndroid Build Coastguard Worker // match v {
275*9860b763SAndroid Build Coastguard Worker // x if x == Self::Variant1 as i32 => Ok(Self::Variant1),
276*9860b763SAndroid Build Coastguard Worker // x if x == Self::Variant2 as i32 => Ok(Self::Variant2),
277*9860b763SAndroid Build Coastguard Worker // x if x == Self::Variant3 as i32 => Ok(Self::Variant3),
278*9860b763SAndroid Build Coastguard Worker // _ => Err( CborError::OutOfRangeIntegerValue),
279*9860b763SAndroid Build Coastguard Worker // }
280*9860b763SAndroid Build Coastguard Worker let recurse = enum_data.variants.iter().map(|variant| {
281*9860b763SAndroid Build Coastguard Worker let vname = &variant.ident;
282*9860b763SAndroid Build Coastguard Worker quote_spanned! {variant.span()=>
283*9860b763SAndroid Build Coastguard Worker x if x == Self::#vname as i32 => Ok(Self::#vname),
284*9860b763SAndroid Build Coastguard Worker }
285*9860b763SAndroid Build Coastguard Worker });
286*9860b763SAndroid Build Coastguard Worker
287*9860b763SAndroid Build Coastguard Worker quote! {
288*9860b763SAndroid Build Coastguard Worker use core::convert::TryInto;
289*9860b763SAndroid Build Coastguard Worker // First get the int value as an `i32`.
290*9860b763SAndroid Build Coastguard Worker let v: i32 = match value {
291*9860b763SAndroid Build Coastguard Worker ciborium::value::Value::Integer(i) => i.try_into().map_err(|_| {
292*9860b763SAndroid Build Coastguard Worker CborError::OutOfRangeIntegerValue
293*9860b763SAndroid Build Coastguard Worker })?,
294*9860b763SAndroid Build Coastguard Worker v => return cbor_type_error(&v, &"int"),
295*9860b763SAndroid Build Coastguard Worker };
296*9860b763SAndroid Build Coastguard Worker // Now match against enum possibilities.
297*9860b763SAndroid Build Coastguard Worker match v {
298*9860b763SAndroid Build Coastguard Worker #(#recurse)*
299*9860b763SAndroid Build Coastguard Worker _ => Err(
300*9860b763SAndroid Build Coastguard Worker CborError::OutOfRangeIntegerValue
301*9860b763SAndroid Build Coastguard Worker ),
302*9860b763SAndroid Build Coastguard Worker }
303*9860b763SAndroid Build Coastguard Worker }
304*9860b763SAndroid Build Coastguard Worker }
305*9860b763SAndroid Build Coastguard Worker Data::Union(_) => unimplemented!(),
306*9860b763SAndroid Build Coastguard Worker }
307*9860b763SAndroid Build Coastguard Worker }
308*9860b763SAndroid Build Coastguard Worker
309*9860b763SAndroid Build Coastguard Worker /// Generate an expression that expresses the CDDL schema for the type.
cddl_struct(name: &Ident, data: &Data) -> TokenStream310*9860b763SAndroid Build Coastguard Worker fn cddl_struct(name: &Ident, data: &Data) -> TokenStream {
311*9860b763SAndroid Build Coastguard Worker match *data {
312*9860b763SAndroid Build Coastguard Worker Data::Struct(ref data) => {
313*9860b763SAndroid Build Coastguard Worker match data.fields {
314*9860b763SAndroid Build Coastguard Worker Fields::Named(ref fields) => {
315*9860b763SAndroid Build Coastguard Worker if fields.named.iter().next().is_none() {
316*9860b763SAndroid Build Coastguard Worker return quote! {
317*9860b763SAndroid Build Coastguard Worker Some(format!("[]"))
318*9860b763SAndroid Build Coastguard Worker };
319*9860b763SAndroid Build Coastguard Worker }
320*9860b763SAndroid Build Coastguard Worker // Expands to an expression like
321*9860b763SAndroid Build Coastguard Worker //
322*9860b763SAndroid Build Coastguard Worker // format!("[
323*9860b763SAndroid Build Coastguard Worker // x: {},
324*9860b763SAndroid Build Coastguard Worker // y: {},
325*9860b763SAndroid Build Coastguard Worker // z: {},
326*9860b763SAndroid Build Coastguard Worker // ]",
327*9860b763SAndroid Build Coastguard Worker // <TypeX>::cddl_ref(),
328*9860b763SAndroid Build Coastguard Worker // <TypeY>::cddl_ref(),
329*9860b763SAndroid Build Coastguard Worker // <TypeZ>::cddl_ref(),
330*9860b763SAndroid Build Coastguard Worker // )
331*9860b763SAndroid Build Coastguard Worker let fmt_recurse = fields.named.iter().map(|f| {
332*9860b763SAndroid Build Coastguard Worker let name = &f.ident;
333*9860b763SAndroid Build Coastguard Worker quote_spanned! {f.span()=>
334*9860b763SAndroid Build Coastguard Worker concat!(" ", stringify!(#name), ": {},\n")
335*9860b763SAndroid Build Coastguard Worker }
336*9860b763SAndroid Build Coastguard Worker });
337*9860b763SAndroid Build Coastguard Worker let fmt = quote! {
338*9860b763SAndroid Build Coastguard Worker concat!("[\n",
339*9860b763SAndroid Build Coastguard Worker #(#fmt_recurse, )*
340*9860b763SAndroid Build Coastguard Worker "]")
341*9860b763SAndroid Build Coastguard Worker };
342*9860b763SAndroid Build Coastguard Worker let recurse = fields.named.iter().map(|f| {
343*9860b763SAndroid Build Coastguard Worker let typ = &f.ty;
344*9860b763SAndroid Build Coastguard Worker quote_spanned! {f.span()=>
345*9860b763SAndroid Build Coastguard Worker <#typ>::cddl_ref()
346*9860b763SAndroid Build Coastguard Worker }
347*9860b763SAndroid Build Coastguard Worker });
348*9860b763SAndroid Build Coastguard Worker quote! {
349*9860b763SAndroid Build Coastguard Worker Some(format!(
350*9860b763SAndroid Build Coastguard Worker #fmt,
351*9860b763SAndroid Build Coastguard Worker #(#recurse, )*
352*9860b763SAndroid Build Coastguard Worker ))
353*9860b763SAndroid Build Coastguard Worker }
354*9860b763SAndroid Build Coastguard Worker }
355*9860b763SAndroid Build Coastguard Worker Fields::Unnamed(ref fields) if fields.unnamed.len() == 1 => {
356*9860b763SAndroid Build Coastguard Worker let inner = fields.unnamed.first().unwrap();
357*9860b763SAndroid Build Coastguard Worker let typ = &inner.ty;
358*9860b763SAndroid Build Coastguard Worker quote! {
359*9860b763SAndroid Build Coastguard Worker Some(<#typ>::cddl_ref())
360*9860b763SAndroid Build Coastguard Worker }
361*9860b763SAndroid Build Coastguard Worker }
362*9860b763SAndroid Build Coastguard Worker Fields::Unnamed(ref fields) => {
363*9860b763SAndroid Build Coastguard Worker if fields.unnamed.iter().next().is_none() {
364*9860b763SAndroid Build Coastguard Worker return quote! {
365*9860b763SAndroid Build Coastguard Worker Some(format!("()"))
366*9860b763SAndroid Build Coastguard Worker };
367*9860b763SAndroid Build Coastguard Worker }
368*9860b763SAndroid Build Coastguard Worker // Expands to an expression like
369*9860b763SAndroid Build Coastguard Worker //
370*9860b763SAndroid Build Coastguard Worker // format!("[
371*9860b763SAndroid Build Coastguard Worker // {},
372*9860b763SAndroid Build Coastguard Worker // {},
373*9860b763SAndroid Build Coastguard Worker // {},
374*9860b763SAndroid Build Coastguard Worker // ]",
375*9860b763SAndroid Build Coastguard Worker // <TypeX>::cddl_ref(),
376*9860b763SAndroid Build Coastguard Worker // <TypeY>::cddl_ref(),
377*9860b763SAndroid Build Coastguard Worker // <TypeZ>::cddl_ref(),
378*9860b763SAndroid Build Coastguard Worker // )
379*9860b763SAndroid Build Coastguard Worker //
380*9860b763SAndroid Build Coastguard Worker let fmt_recurse = fields.unnamed.iter().map(|f| {
381*9860b763SAndroid Build Coastguard Worker quote_spanned! {f.span()=>
382*9860b763SAndroid Build Coastguard Worker " {},\n"
383*9860b763SAndroid Build Coastguard Worker }
384*9860b763SAndroid Build Coastguard Worker });
385*9860b763SAndroid Build Coastguard Worker let fmt = quote! {
386*9860b763SAndroid Build Coastguard Worker concat!("[\n",
387*9860b763SAndroid Build Coastguard Worker #(#fmt_recurse, )*
388*9860b763SAndroid Build Coastguard Worker "]")
389*9860b763SAndroid Build Coastguard Worker };
390*9860b763SAndroid Build Coastguard Worker let recurse = fields.unnamed.iter().map(|f| {
391*9860b763SAndroid Build Coastguard Worker let typ = &f.ty;
392*9860b763SAndroid Build Coastguard Worker quote_spanned! {f.span()=>
393*9860b763SAndroid Build Coastguard Worker <#typ>::cddl_ref()
394*9860b763SAndroid Build Coastguard Worker }
395*9860b763SAndroid Build Coastguard Worker });
396*9860b763SAndroid Build Coastguard Worker quote! {
397*9860b763SAndroid Build Coastguard Worker Some(format!(
398*9860b763SAndroid Build Coastguard Worker #fmt,
399*9860b763SAndroid Build Coastguard Worker #(#recurse, )*
400*9860b763SAndroid Build Coastguard Worker ))
401*9860b763SAndroid Build Coastguard Worker }
402*9860b763SAndroid Build Coastguard Worker }
403*9860b763SAndroid Build Coastguard Worker Fields::Unit => unimplemented!(),
404*9860b763SAndroid Build Coastguard Worker }
405*9860b763SAndroid Build Coastguard Worker }
406*9860b763SAndroid Build Coastguard Worker Data::Enum(ref enum_data) => {
407*9860b763SAndroid Build Coastguard Worker // This only copes with variants with no fields.
408*9860b763SAndroid Build Coastguard Worker // Expands to an expression like:
409*9860b763SAndroid Build Coastguard Worker //
410*9860b763SAndroid Build Coastguard Worker // format!("&(
411*9860b763SAndroid Build Coastguard Worker // EnumName_Variant1: {},
412*9860b763SAndroid Build Coastguard Worker // EnumName_Variant2: {},
413*9860b763SAndroid Build Coastguard Worker // EnumName_Variant3: {},
414*9860b763SAndroid Build Coastguard Worker // )",
415*9860b763SAndroid Build Coastguard Worker // Self::Variant1 as i32,
416*9860b763SAndroid Build Coastguard Worker // Self::Variant2 as i32,
417*9860b763SAndroid Build Coastguard Worker // Self::Variant3 as i32,
418*9860b763SAndroid Build Coastguard Worker // )
419*9860b763SAndroid Build Coastguard Worker //
420*9860b763SAndroid Build Coastguard Worker let fmt_recurse = enum_data.variants.iter().map(|variant| {
421*9860b763SAndroid Build Coastguard Worker let vname = &variant.ident;
422*9860b763SAndroid Build Coastguard Worker quote_spanned! {variant.span()=>
423*9860b763SAndroid Build Coastguard Worker concat!(" ",
424*9860b763SAndroid Build Coastguard Worker stringify!(#name),
425*9860b763SAndroid Build Coastguard Worker "_",
426*9860b763SAndroid Build Coastguard Worker stringify!(#vname),
427*9860b763SAndroid Build Coastguard Worker ": {},\n")
428*9860b763SAndroid Build Coastguard Worker }
429*9860b763SAndroid Build Coastguard Worker });
430*9860b763SAndroid Build Coastguard Worker let fmt = quote! {
431*9860b763SAndroid Build Coastguard Worker concat!("&(\n",
432*9860b763SAndroid Build Coastguard Worker #(#fmt_recurse, )*
433*9860b763SAndroid Build Coastguard Worker ")")
434*9860b763SAndroid Build Coastguard Worker };
435*9860b763SAndroid Build Coastguard Worker let recurse = enum_data.variants.iter().map(|variant| {
436*9860b763SAndroid Build Coastguard Worker let vname = &variant.ident;
437*9860b763SAndroid Build Coastguard Worker quote_spanned! {variant.span()=>
438*9860b763SAndroid Build Coastguard Worker Self::#vname as i32
439*9860b763SAndroid Build Coastguard Worker }
440*9860b763SAndroid Build Coastguard Worker });
441*9860b763SAndroid Build Coastguard Worker quote! {
442*9860b763SAndroid Build Coastguard Worker Some(format!(
443*9860b763SAndroid Build Coastguard Worker #fmt,
444*9860b763SAndroid Build Coastguard Worker #(#recurse, )*
445*9860b763SAndroid Build Coastguard Worker ))
446*9860b763SAndroid Build Coastguard Worker }
447*9860b763SAndroid Build Coastguard Worker }
448*9860b763SAndroid Build Coastguard Worker Data::Union(_) => unimplemented!(),
449*9860b763SAndroid Build Coastguard Worker }
450*9860b763SAndroid Build Coastguard Worker }
451*9860b763SAndroid Build Coastguard Worker
452*9860b763SAndroid Build Coastguard Worker /// Derive macro that implements a `from_raw_tag_value` method for the `Tag` enum.
453*9860b763SAndroid Build Coastguard Worker #[proc_macro_derive(FromRawTag)]
derive_from_raw_tag(input: proc_macro::TokenStream) -> proc_macro::TokenStream454*9860b763SAndroid Build Coastguard Worker pub fn derive_from_raw_tag(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
455*9860b763SAndroid Build Coastguard Worker let input = parse_macro_input!(input as DeriveInput);
456*9860b763SAndroid Build Coastguard Worker derive_from_raw_tag_internal(&input)
457*9860b763SAndroid Build Coastguard Worker }
458*9860b763SAndroid Build Coastguard Worker
derive_from_raw_tag_internal(input: &DeriveInput) -> proc_macro::TokenStream459*9860b763SAndroid Build Coastguard Worker fn derive_from_raw_tag_internal(input: &DeriveInput) -> proc_macro::TokenStream {
460*9860b763SAndroid Build Coastguard Worker let name = &input.ident;
461*9860b763SAndroid Build Coastguard Worker let from_val = from_raw_tag(name, &input.data);
462*9860b763SAndroid Build Coastguard Worker let expanded = quote! {
463*9860b763SAndroid Build Coastguard Worker pub fn from_raw_tag_value(raw_tag: u32) -> #name {
464*9860b763SAndroid Build Coastguard Worker #from_val
465*9860b763SAndroid Build Coastguard Worker }
466*9860b763SAndroid Build Coastguard Worker };
467*9860b763SAndroid Build Coastguard Worker expanded.into()
468*9860b763SAndroid Build Coastguard Worker }
469*9860b763SAndroid Build Coastguard Worker
470*9860b763SAndroid Build Coastguard Worker /// Generate an expression to convert a `u32` into an instance of an fieldless enum.
471*9860b763SAndroid Build Coastguard Worker /// Assumes the existence of an `Invalid` variant as a fallback, and assumes that a
472*9860b763SAndroid Build Coastguard Worker /// `raw_tag_value` function is in scope.
from_raw_tag(name: &Ident, data: &Data) -> TokenStream473*9860b763SAndroid Build Coastguard Worker fn from_raw_tag(name: &Ident, data: &Data) -> TokenStream {
474*9860b763SAndroid Build Coastguard Worker match data {
475*9860b763SAndroid Build Coastguard Worker Data::Enum(enum_data) => {
476*9860b763SAndroid Build Coastguard Worker let recurse = enum_data.variants.iter().map(|variant| {
477*9860b763SAndroid Build Coastguard Worker let vname = &variant.ident;
478*9860b763SAndroid Build Coastguard Worker quote_spanned! {variant.span()=>
479*9860b763SAndroid Build Coastguard Worker x if x == raw_tag_value(#name::#vname) => #name::#vname,
480*9860b763SAndroid Build Coastguard Worker }
481*9860b763SAndroid Build Coastguard Worker });
482*9860b763SAndroid Build Coastguard Worker
483*9860b763SAndroid Build Coastguard Worker quote! {
484*9860b763SAndroid Build Coastguard Worker match raw_tag {
485*9860b763SAndroid Build Coastguard Worker #(#recurse)*
486*9860b763SAndroid Build Coastguard Worker _ => #name::Invalid,
487*9860b763SAndroid Build Coastguard Worker }
488*9860b763SAndroid Build Coastguard Worker }
489*9860b763SAndroid Build Coastguard Worker }
490*9860b763SAndroid Build Coastguard Worker _ => unimplemented!(),
491*9860b763SAndroid Build Coastguard Worker }
492*9860b763SAndroid Build Coastguard Worker }
493*9860b763SAndroid Build Coastguard Worker
494*9860b763SAndroid Build Coastguard Worker /// Derive macro that implements the `legacy::InnerSerialize` trait. Using this macro requires
495*9860b763SAndroid Build Coastguard Worker /// that `InnerSerialize` and `Error` from `kmr_wire::legacy` be locally `use`d.
496*9860b763SAndroid Build Coastguard Worker #[proc_macro_derive(LegacySerialize)]
derive_legacy_serialize(input: proc_macro::TokenStream) -> proc_macro::TokenStream497*9860b763SAndroid Build Coastguard Worker pub fn derive_legacy_serialize(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
498*9860b763SAndroid Build Coastguard Worker let input = parse_macro_input!(input as DeriveInput);
499*9860b763SAndroid Build Coastguard Worker derive_legacy_serialize_internal(&input)
500*9860b763SAndroid Build Coastguard Worker }
501*9860b763SAndroid Build Coastguard Worker
derive_legacy_serialize_internal(input: &DeriveInput) -> proc_macro::TokenStream502*9860b763SAndroid Build Coastguard Worker fn derive_legacy_serialize_internal(input: &DeriveInput) -> proc_macro::TokenStream {
503*9860b763SAndroid Build Coastguard Worker let name = &input.ident;
504*9860b763SAndroid Build Coastguard Worker
505*9860b763SAndroid Build Coastguard Worker let deserialize_val = deserialize_struct(&input.data);
506*9860b763SAndroid Build Coastguard Worker let serialize_val = serialize_struct(&input.data);
507*9860b763SAndroid Build Coastguard Worker
508*9860b763SAndroid Build Coastguard Worker let expanded = quote! {
509*9860b763SAndroid Build Coastguard Worker impl InnerSerialize for #name {
510*9860b763SAndroid Build Coastguard Worker fn deserialize(data: &[u8]) -> Result<(Self, &[u8]), Error> {
511*9860b763SAndroid Build Coastguard Worker #deserialize_val
512*9860b763SAndroid Build Coastguard Worker }
513*9860b763SAndroid Build Coastguard Worker fn serialize_into(&self, buf: &mut Vec<u8>) -> Result<(), Error> {
514*9860b763SAndroid Build Coastguard Worker #serialize_val
515*9860b763SAndroid Build Coastguard Worker }
516*9860b763SAndroid Build Coastguard Worker }
517*9860b763SAndroid Build Coastguard Worker };
518*9860b763SAndroid Build Coastguard Worker
519*9860b763SAndroid Build Coastguard Worker expanded.into()
520*9860b763SAndroid Build Coastguard Worker }
521*9860b763SAndroid Build Coastguard Worker
deserialize_struct(data: &Data) -> TokenStream522*9860b763SAndroid Build Coastguard Worker fn deserialize_struct(data: &Data) -> TokenStream {
523*9860b763SAndroid Build Coastguard Worker match data {
524*9860b763SAndroid Build Coastguard Worker Data::Struct(ref data) => {
525*9860b763SAndroid Build Coastguard Worker match data.fields {
526*9860b763SAndroid Build Coastguard Worker Fields::Named(ref fields) => {
527*9860b763SAndroid Build Coastguard Worker // Expands to an expression like
528*9860b763SAndroid Build Coastguard Worker //
529*9860b763SAndroid Build Coastguard Worker // let (x, data) = <XType>::deserialize(data)?;
530*9860b763SAndroid Build Coastguard Worker // let (y, data) = <YType>::deserialize(data)?;
531*9860b763SAndroid Build Coastguard Worker // let (z, data) = <ZType>::deserialize(data)?;
532*9860b763SAndroid Build Coastguard Worker // Ok((Self {
533*9860b763SAndroid Build Coastguard Worker // x,
534*9860b763SAndroid Build Coastguard Worker // y,
535*9860b763SAndroid Build Coastguard Worker // z,
536*9860b763SAndroid Build Coastguard Worker // }, data))
537*9860b763SAndroid Build Coastguard Worker //
538*9860b763SAndroid Build Coastguard Worker let recurse1 = fields.named.iter().map(|f| {
539*9860b763SAndroid Build Coastguard Worker let name = &f.ident;
540*9860b763SAndroid Build Coastguard Worker let typ = &f.ty;
541*9860b763SAndroid Build Coastguard Worker quote_spanned! {f.span()=>
542*9860b763SAndroid Build Coastguard Worker let (#name, data) = <#typ>::deserialize(data)?;
543*9860b763SAndroid Build Coastguard Worker }
544*9860b763SAndroid Build Coastguard Worker });
545*9860b763SAndroid Build Coastguard Worker let recurse2 = fields.named.iter().map(|f| {
546*9860b763SAndroid Build Coastguard Worker let name = &f.ident;
547*9860b763SAndroid Build Coastguard Worker quote_spanned! {f.span()=>
548*9860b763SAndroid Build Coastguard Worker #name
549*9860b763SAndroid Build Coastguard Worker }
550*9860b763SAndroid Build Coastguard Worker });
551*9860b763SAndroid Build Coastguard Worker quote! {
552*9860b763SAndroid Build Coastguard Worker #(#recurse1)*
553*9860b763SAndroid Build Coastguard Worker Ok((Self {
554*9860b763SAndroid Build Coastguard Worker #(#recurse2, )*
555*9860b763SAndroid Build Coastguard Worker }, data))
556*9860b763SAndroid Build Coastguard Worker }
557*9860b763SAndroid Build Coastguard Worker }
558*9860b763SAndroid Build Coastguard Worker Fields::Unnamed(_) => unimplemented!(),
559*9860b763SAndroid Build Coastguard Worker Fields::Unit => unimplemented!(),
560*9860b763SAndroid Build Coastguard Worker }
561*9860b763SAndroid Build Coastguard Worker }
562*9860b763SAndroid Build Coastguard Worker Data::Enum(_) => unimplemented!(),
563*9860b763SAndroid Build Coastguard Worker Data::Union(_) => unimplemented!(),
564*9860b763SAndroid Build Coastguard Worker }
565*9860b763SAndroid Build Coastguard Worker }
566*9860b763SAndroid Build Coastguard Worker
serialize_struct(data: &Data) -> TokenStream567*9860b763SAndroid Build Coastguard Worker fn serialize_struct(data: &Data) -> TokenStream {
568*9860b763SAndroid Build Coastguard Worker match data {
569*9860b763SAndroid Build Coastguard Worker Data::Struct(ref data) => {
570*9860b763SAndroid Build Coastguard Worker match data.fields {
571*9860b763SAndroid Build Coastguard Worker Fields::Named(ref fields) => {
572*9860b763SAndroid Build Coastguard Worker // Expands to an expression like
573*9860b763SAndroid Build Coastguard Worker //
574*9860b763SAndroid Build Coastguard Worker // self.x.serialize_into(buf)?;
575*9860b763SAndroid Build Coastguard Worker // self.y.serialize_into(buf)?;
576*9860b763SAndroid Build Coastguard Worker // self.z.serialize_into(buf)?;
577*9860b763SAndroid Build Coastguard Worker // Ok(())
578*9860b763SAndroid Build Coastguard Worker //
579*9860b763SAndroid Build Coastguard Worker let recurse = fields.named.iter().map(|f| {
580*9860b763SAndroid Build Coastguard Worker let name = &f.ident;
581*9860b763SAndroid Build Coastguard Worker quote_spanned! {f.span()=>
582*9860b763SAndroid Build Coastguard Worker self.#name.serialize_into(buf)?;
583*9860b763SAndroid Build Coastguard Worker }
584*9860b763SAndroid Build Coastguard Worker });
585*9860b763SAndroid Build Coastguard Worker quote! {
586*9860b763SAndroid Build Coastguard Worker #(#recurse)*
587*9860b763SAndroid Build Coastguard Worker Ok(())
588*9860b763SAndroid Build Coastguard Worker }
589*9860b763SAndroid Build Coastguard Worker }
590*9860b763SAndroid Build Coastguard Worker Fields::Unnamed(_) => unimplemented!(),
591*9860b763SAndroid Build Coastguard Worker Fields::Unit => unimplemented!(),
592*9860b763SAndroid Build Coastguard Worker }
593*9860b763SAndroid Build Coastguard Worker }
594*9860b763SAndroid Build Coastguard Worker Data::Enum(_) => unimplemented!(),
595*9860b763SAndroid Build Coastguard Worker Data::Union(_) => unimplemented!(),
596*9860b763SAndroid Build Coastguard Worker }
597*9860b763SAndroid Build Coastguard Worker }
598