1#![recursion_limit="128"]
2
3extern crate proc_macro;
4extern crate proc_macro2;
5#[macro_use]
6extern crate quote;
7extern crate syn;
8
9use proc_macro::TokenStream;
10use proc_macro2::Span;
11use syn::{Data, Fields, Ident, Lit, Meta, NestedMeta};
12
13#[proc_macro_derive(Header, attributes(header))]
14pub fn derive_header(input: TokenStream) -> TokenStream {
15 let ast = syn::parse(input).unwrap();
16 impl_header(&ast).into()
17}
18
19fn impl_header(ast: &syn::DeriveInput) -> proc_macro2::TokenStream {
20 let fns = match impl_fns(ast) {
21 Ok(fns) => fns,
22 Err(msg) => {
23 return quote! {
24 compile_error!(#msg);
25 }.into();
26 }
27 };
28
29 let decode = fns.decode;
30 let encode = fns.encode;
31
32 let ty = &ast.ident;
33 let hname = fns.name.unwrap_or_else(|| {
34 to_header_name(&ty.to_string())
35 });
36 let hname_ident = Ident::new(&hname, Span::call_site());
37 let dummy_const = Ident::new(&format!("_IMPL_HEADER_FOR_{}", hname), Span::call_site());
38 let impl_block = quote! {
39 impl __hc::Header for #ty {
40 fn name() -> &'static __hc::HeaderName {
41 &__hc::header::#hname_ident
42 }
43 fn decode<'i, I>(values: &mut I) -> Result<Self, __hc::Error>
44 where
45 I: Iterator<Item = &'i __hc::HeaderValue>,
46 {
47 #decode
48 }
49 fn encode<E: Extend<__hc::HeaderValue>>(&self, values: &mut E) {
50 #encode
51 }
52 }
53 };
54
55 quote! {
56 const #dummy_const: () = {
57 extern crate headers_core as __hc;
58 #impl_block
59 };
60 }
61}
62
63struct Fns {
64 encode: proc_macro2::TokenStream,
65 decode: proc_macro2::TokenStream,
66 name: Option<String>,
67}
68
69fn impl_fns(ast: &syn::DeriveInput) -> Result<Fns, String> {
70 let ty = &ast.ident;
71
72 let st = match ast.data {
74 Data::Struct(ref st) => st,
75 _ => {
76 return Err("derive(Header) only works on structs".into())
77 }
78 };
79
80 let mut name = None;
84 for attr in &ast.attrs {
85 if attr.path.segments.len() != 1 {
86 continue;
87 }
88 if attr.path.segments[0].ident != "header" {
89 continue;
90 }
91
92 match attr.parse_meta() {
93 Ok(Meta::List(list)) => {
94 for meta in &list.nested {
95 match meta {
96 NestedMeta::Meta(Meta::NameValue(ref kv)) if kv.path.is_ident("name_const") => {
104 if name.is_some() {
105 return Err("repeated 'name_const' option in #[header] attribute".into());
106 }
107 name = match kv.lit {
108 Lit::Str(ref s) => Some(s.value()),
109 _ => {
110 return Err("illegal literal in #[header(name_const = ..)] attribute".into());
111 }
112 };
113 }
114 _ => {
115 return Err("illegal option in #[header(..)] attribute".into())
116 }
117
118 }
119 }
120
121 },
122 Ok(Meta::NameValue(_)) => {
123 return Err("illegal #[header = ..] attribute".into())
124 },
125 Ok(Meta::Path(_)) => {
126 return Err("empty #[header] attributes do nothing".into())
127 },
128 Err(e) => {
129 return Err(format!("illegal #[header ??] attribute: {:?}", e))
131 }
132 }
133 }
134
135 let decode_res = quote! {
136 ::util::TryFromValues::try_from_values(values)
137 };
138
139 let (decode, encode_name) = match st.fields {
140 Fields::Named(ref fields) => {
141 if fields.named.len() != 1 {
142 return Err("derive(Header) doesn't support multiple fields".into());
143 }
144
145 let field = fields
146 .named
147 .iter()
148 .next()
149 .expect("just checked for len() == 1");
150 let field_name = field.ident.as_ref().unwrap();
151
152 let decode = quote! {
153 #decode_res
154 .map(|inner| #ty {
155 #field_name: inner,
156 })
157 };
158
159 let encode_name = Ident::new(&field_name.to_string(), Span::call_site());
160 (decode, Value::Named(encode_name))
161 },
162 Fields::Unnamed(ref fields) => {
163 if fields.unnamed.len() != 1 {
164 return Err("derive(Header) doesn't support multiple fields".into());
165 }
166
167 let decode = quote! {
168 #decode_res
169 .map(#ty)
170 };
171
172 (decode, Value::Unnamed)
173 },
174 Fields::Unit => {
175 return Err("derive(Header) doesn't support unit structs".into())
176 }
177 };
178
179 let encode = {
200 let field = if let Value::Named(field) = encode_name {
201 quote! {
202 (&self.#field)
203 }
204 } else {
205 quote! {
206 (&self.0)
207 }
208 };
209 quote! {
210 values.extend(::std::iter::once((#field).into()));
211 }
212 };
213
214 Ok(Fns {
215 decode,
216 encode,
217 name,
218 })
219}
220
221fn to_header_name(ty_name: &str) -> String {
222 let mut out = String::new();
223 let mut first = true;
224 for c in ty_name.chars() {
225 if first {
226 out.push(c.to_ascii_uppercase());
227 first = false;
228 } else {
229 if c.is_uppercase() {
230 out.push('_');
231 }
232 out.push(c.to_ascii_uppercase());
233 }
234 }
235 out
236}
237
238enum Value {
239 Named(Ident),
240 Unnamed,
241}
242