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}