flexiber_derive/
lib.rs

1//! Custom derive support for the `flexiber` crate
2//!
3//! With `#[tlv(slice)]` set, `Encodable` should work for fields implementing `AsRef<[u8]>`,
4//! and `Decodable` should work for fields implementing `TryFrom<[u8]>`, even if the field
5//! is not `Decodable` or `Encodable`.
6
7#![crate_type = "proc-macro"]
8#![warn(rust_2018_idioms, trivial_casts, unused_qualifications)]
9
10mod decodable;
11use decodable::DeriveDecodableStruct;
12mod encodable;
13use encodable::DeriveEncodableStruct;
14
15use proc_macro2::TokenStream;
16use syn::{Attribute, Field, Ident, LitStr, Token};
17use synstructure::{decl_derive, Structure};
18
19decl_derive!(
20    [Decodable, attributes(tlv)] =>
21
22    /// Derive the [`Decodable`][1] trait on a struct.
23    ///
24    /// See [toplevel documentation for the `flexiber_derive` crate][2] for more
25    /// information about how to use this macro.
26    ///
27    /// [1]: https://docs.rs/flexiber/latest/flexiber/trait.Decodable.html
28    /// [2]: https://docs.rs/flexiber_derive/
29    derive_decodable
30);
31
32decl_derive!(
33    [Encodable, attributes(tlv)] =>
34
35    /// Derive the [`Encodable`][1] trait on a struct.
36    ///
37    /// See [toplevel documentation for the `flexiber_derive` crate][2] for more
38    /// information about how to use this macro.
39    ///
40    /// [1]: https://docs.rs/flexiber/latest/flexiber/trait.Decodable.html
41    /// [2]: https://docs.rs/flexiber_derive/
42    derive_encodable
43);
44
45/// Custom derive for `flexiber::Decodable`
46fn derive_decodable(s: Structure<'_>) -> TokenStream {
47    let ast = s.ast();
48
49    // TODO: enum support
50    match &ast.data {
51        syn::Data::Struct(data) => DeriveDecodableStruct::derive(s, data, &ast.ident, &ast.attrs),
52        other => panic!("can't derive `Decodable` on: {:?}", other),
53    }
54}
55
56/// Custom derive for `flexiber::Encodable`
57fn derive_encodable(s: Structure<'_>) -> TokenStream {
58    let ast = s.ast();
59
60    // TODO: enum support
61    match &ast.data {
62        syn::Data::Struct(data) => DeriveEncodableStruct::derive(s, data, &ast.ident, &ast.attrs),
63        other => panic!("can't derive `Encodable` on: {:?}", other),
64    }
65}
66
67#[derive(Clone, Copy, Debug, Default)]
68struct BerTag {
69    class: Class,
70    constructed: bool,
71    number: u16,
72}
73
74#[derive(Clone, Copy, Debug, Default)]
75struct SimpleTag(u8);
76
77#[derive(Clone, Copy, Debug)]
78enum Tag {
79    Ber(BerTag),
80    Simple(SimpleTag),
81}
82
83impl From<BerTag> for Tag {
84    fn from(tag: BerTag) -> Self {
85        Self::Ber(tag)
86    }
87}
88
89impl From<SimpleTag> for Tag {
90    fn from(tag: SimpleTag) -> Self {
91        Self::Simple(tag)
92    }
93}
94
95impl Default for Tag {
96    fn default() -> Self {
97        Self::Ber(BerTag::default())
98    }
99}
100
101#[derive(Clone, Copy, Debug)]
102#[repr(u8)]
103enum Class {
104    Universal = 0b00,
105    Application = 0b01,
106    Context = 0b10,
107    Private = 0b11,
108}
109
110impl Default for Class {
111    fn default() -> Self {
112        Class::Universal
113    }
114}
115
116/// Attributes of a field
117#[derive(Debug)]
118struct FieldAttrs {
119    /// Name of the field
120    pub name: Ident,
121
122    /// Value of tag to use
123    pub tag: Tag,
124
125    /// Whether the `#[tlv(slice)]` attribute was set
126    pub slice: bool,
127}
128
129impl FieldAttrs {
130    /// Parse the attributes of a field
131    fn new(field: &Field) -> Self {
132        let name = field
133            .ident
134            .as_ref()
135            .cloned()
136            .expect("no name on struct field i.e. tuple structs unsupported");
137
138        let (tag, slice) = extract_attrs(&name, &field.attrs);
139
140        Self { name, tag, slice }
141    }
142}
143
144fn extract_attrs_optional_tag(name: &Ident, attrs: &[Attribute]) -> (Option<Tag>, bool) {
145    let mut tag = Tag::default();
146    let mut tag_number_is_set = false;
147    let mut slice = false;
148
149    for attr in attrs {
150        if !attr.path().is_ident("tlv") {
151            continue;
152        }
153
154        attr.parse_nested_meta(|meta| {
155            let path = meta.path;
156            if path.is_ident("slice") {
157                slice = true;
158            } else if path.is_ident("universal") {
159                tag = {
160                    let mut tag = if let Tag::Ber(tag) = tag {
161                        tag
162                    } else {
163                        Default::default()
164                    };
165                    tag.class = Class::Universal;
166                    tag.into()
167                };
168            } else if path.is_ident("application") {
169                tag = {
170                    let mut tag = if let Tag::Ber(tag) = tag {
171                        tag
172                    } else {
173                        Default::default()
174                    };
175                    tag.class = Class::Application;
176                    tag.into()
177                };
178            } else if path.is_ident("context") {
179                tag = {
180                    let mut tag = if let Tag::Ber(tag) = tag {
181                        tag
182                    } else {
183                        Default::default()
184                    };
185                    tag.class = Class::Context;
186                    tag.into()
187                };
188            } else if path.is_ident("private") {
189                tag = {
190                    let mut tag = if let Tag::Ber(tag) = tag {
191                        tag
192                    } else {
193                        Default::default()
194                    };
195                    tag.class = Class::Private;
196                    tag.into()
197                };
198            } else if path.is_ident("constructed") {
199                tag = {
200                    let mut tag = if let Tag::Ber(tag) = tag {
201                        tag
202                    } else {
203                        Default::default()
204                    };
205                    tag.constructed = true;
206                    tag.into()
207                };
208            } else if path.is_ident("primitive") {
209                tag = {
210                    let mut tag = if let Tag::Ber(tag) = tag {
211                        tag
212                    } else {
213                        Default::default()
214                    };
215                    tag.constructed = false;
216                    tag.into()
217                };
218            } else if path.is_ident("number") {
219                tag = {
220                    if !meta.input.peek(Token![=]) || !meta.input.peek2(LitStr) {
221                        panic!("Malformed TLV attribute");
222                    }
223                    let _: Token![=] = meta.input.parse().expect("unreachable");
224                    let lit_str: LitStr = meta.input.parse().expect("unreachable");
225
226                    let possibly_with_prefix = lit_str.value();
227                    let without_prefix = possibly_with_prefix.trim_start_matches("0x");
228                    let tag_number = u16::from_str_radix(without_prefix, 16)
229                        .expect("tag values must be between one and 254");
230                    let mut tag = if let Tag::Ber(tag) = tag {
231                        tag
232                    } else {
233                        Default::default()
234                    };
235                    tag.number = tag_number;
236                    tag_number_is_set = true;
237                    tag.into()
238                }
239            } else if path.is_ident("simple") {
240                tag = {
241                    if !meta.input.peek(Token![=]) || !meta.input.peek2(LitStr) {
242                        panic!("Malformed TLV attribute");
243                    }
244                    let _: Token![=] = meta.input.parse().expect("unreachable");
245                    let lit_str: LitStr = meta.input.parse().expect("unreachable");
246
247                    let possibly_with_prefix = lit_str.value();
248                    let without_prefix = possibly_with_prefix.trim_start_matches("0x");
249                    let tag_number = u8::from_str_radix(without_prefix, 16)
250                        .expect("tag values must be between one and 254");
251                    let mut tag = if let Tag::Simple(tag) = tag {
252                        tag
253                    } else {
254                        Default::default()
255                    };
256                    tag.0 = tag_number;
257                    tag_number_is_set = true;
258                    tag.into()
259                };
260            } else {
261                panic!("unknown `tlv` attribute for field `{}`: {:?}", name, path);
262            }
263            Ok(())
264        })
265        .unwrap();
266
267        // match attr.parse_meta().expect("error parsing `tlv` attribute") {
268        //     Meta::List(MetaList { nested, .. }) if !nested.is_empty() => {
269        //         for entry in nested {
270        //             match entry {
271        //                 NestedMeta::Meta(Meta::Path(path)) => {
272        //                     if path.is_ident("slice") {
273        //                         slice = true;
274        //                     } else if path.is_ident("universal") {
275        //                         tag = {
276        //                             let mut tag = if let Tag::Ber(tag) = tag {
277        //                                 tag
278        //                             } else {
279        //                                 Default::default()
280        //                             };
281        //                             tag.class = Class::Universal;
282        //                             tag.into()
283        //                         };
284        //                     } else if path.is_ident("application") {
285        //                         tag = {
286        //                             let mut tag = if let Tag::Ber(tag) = tag {
287        //                                 tag
288        //                             } else {
289        //                                 Default::default()
290        //                             };
291        //                             tag.class = Class::Application;
292        //                             tag.into()
293        //                         };
294        //                     } else if path.is_ident("context") {
295        //                         tag = {
296        //                             let mut tag = if let Tag::Ber(tag) = tag {
297        //                                 tag
298        //                             } else {
299        //                                 Default::default()
300        //                             };
301        //                             tag.class = Class::Context;
302        //                             tag.into()
303        //                         };
304        //                     } else if path.is_ident("private") {
305        //                         tag = {
306        //                             let mut tag = if let Tag::Ber(tag) = tag {
307        //                                 tag
308        //                             } else {
309        //                                 Default::default()
310        //                             };
311        //                             tag.class = Class::Private;
312        //                             tag.into()
313        //                         };
314        //                     } else if path.is_ident("constructed") {
315        //                         tag = {
316        //                             let mut tag = if let Tag::Ber(tag) = tag {
317        //                                 tag
318        //                             } else {
319        //                                 Default::default()
320        //                             };
321        //                             tag.constructed = true;
322        //                             tag.into()
323        //                         };
324        //                     } else if path.is_ident("primitive") {
325        //                         tag = {
326        //                             let mut tag = if let Tag::Ber(tag) = tag {
327        //                                 tag
328        //                             } else {
329        //                                 Default::default()
330        //                             };
331        //                             tag.constructed = false;
332        //                             tag.into()
333        //                         };
334        //                     } else {
335        //                         panic!("unknown `tlv` attribute for field `{}`: {:?}", name, path);
336        //                     }
337        //                 }
338        //                 NestedMeta::Meta(Meta::NameValue(MetaNameValue {
339        //                     path,
340        //                     lit: Lit::Str(lit_str),
341        //                     ..
342        //                 })) => {
343        //                     // Parse the `type = "..."` attribute
344        //                     if path.is_ident("number") {
345        //                         tag = {
346        //                             let possibly_with_prefix = lit_str.value();
347        //                             let without_prefix =
348        //                                 possibly_with_prefix.trim_start_matches("0x");
349        //                             let tag_number = u16::from_str_radix(without_prefix, 16)
350        //                                 .expect("tag values must be between one and 254");
351        //                             let mut tag = if let Tag::Ber(tag) = tag {
352        //                                 tag
353        //                             } else {
354        //                                 Default::default()
355        //                             };
356        //                             tag.number = tag_number;
357        //                             tag_number_is_set = true;
358        //                             tag.into()
359        //                         }
360        //                     } else if path.is_ident("simple") {
361        //                         tag = {
362        //                             let possibly_with_prefix = lit_str.value();
363        //                             let without_prefix =
364        //                                 possibly_with_prefix.trim_start_matches("0x");
365        //                             let tag_number = u8::from_str_radix(without_prefix, 16)
366        //                                 .expect("tag values must be between one and 254");
367        //                             let mut tag = if let Tag::Simple(tag) = tag {
368        //                                 tag
369        //                             } else {
370        //                                 Default::default()
371        //                             };
372        //                             tag.0 = tag_number;
373        //                             tag_number_is_set = true;
374        //                             tag.into()
375        //                         };
376        //                     } else {
377        //                         panic!("unknown `tlv` attribute for field `{}`: {:?}", name, path);
378        //                     }
379        //                 }
380        //                 other => panic!(
381        //                     "a malformed `tlv` attribute for field `{}`: {:?}",
382        //                     name, other
383        //                 ),
384        //             }
385        //         }
386        //     }
387        //     other => panic!(
388        //         "malformed `tlv` attribute for field `{}`: {:#?}",
389        //         name, other
390        //     ),
391        // }
392    }
393
394    if tag_number_is_set {
395        (Some(tag), slice)
396    } else {
397        (None, slice)
398    }
399}
400
401fn extract_attrs(name: &Ident, attrs: &[Attribute]) -> (Tag, bool) {
402    let (tag, slice) = extract_attrs_optional_tag(name, attrs);
403
404    if let Some(tag) = tag {
405        (tag, slice)
406    } else {
407        panic!("BER-TLV tag missing for `{}`", name);
408    }
409}