1#![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
15
16use proc_macro2::TokenStream;
17use syn::{
18 Attribute, Field, Ident, Lit, Meta, MetaList, MetaNameValue, NestedMeta,
19};
20use synstructure::{decl_derive, Structure};
21
22decl_derive!(
23 [Decodable, attributes(tlv)] =>
24
25 derive_decodable
33);
34
35decl_derive!(
36 [Encodable, attributes(tlv)] =>
37
38 derive_encodable
46);
47
48fn derive_decodable(s: Structure<'_>) -> TokenStream {
50 let ast = s.ast();
51
52 match &ast.data {
54 syn::Data::Struct(data) => DeriveDecodableStruct::derive(s, data, &ast.ident, &ast.attrs),
55 other => panic!("can't derive `Decodable` on: {:?}", other),
56 }
57}
58
59fn derive_encodable(s: Structure<'_>) -> TokenStream {
61 let ast = s.ast();
62
63 match &ast.data {
65 syn::Data::Struct(data) => DeriveEncodableStruct::derive(s, data, &ast.ident, &ast.attrs),
66 other => panic!("can't derive `Encodable` on: {:?}", other),
67 }
68}
69
70#[derive(Debug)]
72struct FieldAttrs {
73 pub name: Ident,
75
76 pub tag: u8,
78
79 pub slice: bool
81}
82
83impl FieldAttrs {
84 fn new(field: &Field) -> Self {
86 let name = field
87 .ident
88 .as_ref()
89 .cloned()
90 .expect("no name on struct field i.e. tuple structs unsupported");
91
92 let (tag, slice) = extract_attrs(&name, &field.attrs);
93
94 Self { name, tag, slice }
95 }
96}
97
98fn extract_attrs_optional_tag(name: &Ident, attrs: &[Attribute]) -> (Option<u8>, bool) {
99 let mut tag = None;
100 let mut slice = false;
101
102 for attr in attrs {
103 if !attr.path.is_ident("tlv") {
104 continue;
105 }
106
107 match attr.parse_meta().expect("error parsing `tlv` attribute") {
108 Meta::List(MetaList { nested, .. }) if !nested.is_empty() => {
109 for entry in nested {
110 match entry {
111 NestedMeta::Meta(Meta::Path(path)) => {
112 if !path.is_ident("slice") {
113 panic!("unknown `tlv` attribute for field `{}`: {:?}", name, path);
114 }
115 slice = true;
116 }
117 NestedMeta::Meta(Meta::NameValue(MetaNameValue {
118 path,
119 lit: Lit::Str(lit_str),
120 ..
121 })) => {
122 if !path.is_ident("tag") {
124 panic!("unknown `tlv` attribute for field `{}`: {:?}", name, path);
125 }
126
127 if tag.is_some() {
128 panic!("duplicate SIMPLE-TLV `tag` attribute for field: {}", name);
129 }
130
131 let possibly_with_prefix = lit_str.value();
132 let without_prefix = possibly_with_prefix.trim_start_matches("0x");
133 let tag_value = u8::from_str_radix(without_prefix, 16).expect("tag values must be between one and 254");
134 if tag_value == 0 || tag_value == 255 {
135 panic!("SIMPLE-TLV tags must not be zero or 255");
136 }
137 tag = Some(tag_value);
138 }
139 other => panic!(
140 "a malformed `tlv` attribute for field `{}`: {:?}",
141 name, other
142 ),
143 }
144 }
145 }
146 other => panic!(
147 "malformed `tlv` attribute for field `{}`: {:#?}",
148 name, other
149 ),
150 }
151 }
152
153 (tag, slice)
154}
155
156fn extract_attrs(name: &Ident, attrs: &[Attribute]) -> (u8, bool) {
157 let (tag, slice) = extract_attrs_optional_tag(name, attrs);
158
159 if let Some(tag) = tag {
160 (tag, slice)
161 } else {
162 panic!("SIMPLE-TLV tag missing for `{}`", name);
163 }
164}