1extern crate proc_macro;
2
3use crate::proc_macro::TokenStream;
4use quote::{quote, ToTokens};
5use syn::{
6 self,
7 Data,
8 DataStruct,
9 DeriveInput,
10 Error,
11 Field,
12 Fields,
13 export::Span,
14 LitStr,
15 punctuated::Punctuated,
16 spanned::Spanned,
17 token::Comma
18};
19
20
21fn extract_struct_fields<'a>(data: &'a Data, span: &Span) -> Result<&'a DataStruct, Error> {
22 match data {
23 Data::Struct(struct_fields) => Ok(struct_fields),
24 _ => Err( Error::new(span.clone(), "cannot derive json type for non structs"))
25 }
26}
27
28fn extract_named_fields<'a>(data_struct: &'a DataStruct,
29 span: &Span) -> Result<Option<&'a Punctuated<Field, Comma>>, Error> {
30 match data_struct.fields {
31 Fields::Named(ref named_fields) => Ok(Some(&named_fields.named)),
32 Fields::Unnamed(_) => Err( Error::new(span.clone(), "cannot derive json type for a tuple struct")),
33 Fields::Unit => Ok(None)
34 }
35}
36
37
38fn fields_to_json(data: &Data, span: &Span) -> Result<String, Error> {
39 let data_struct = extract_struct_fields(data, span)?;
40 if let Some(named_fields) = extract_named_fields(data_struct, span)? {
41
42 let mut fields = Vec::new();
43 for field in named_fields {
44 let ident = field.ident
45 .as_ref()
46 .ok_or(Error::new(span.clone(), "cannot derive json type for unnamed field"))?
47 .to_string();
48
49 let ty = field.ty
50 .clone()
51 .into_token_stream()
52 .to_string()
53 .replace(" ", "");
54
55 fields.push(format!("{{\"{}\": \"{}\"}}", ident, ty));
56 fields.push(String::from(", "));
57 }
58
59 let _ = fields.pop();
60
61 let mut json = String::from("[");
62 for field in fields.iter() {
63 json.push_str(&*field);
64 }
65 json.push(']');
66
67 Ok(json)
68 }
69 else {
70 Ok("[]".to_owned())
71 }
72}
73
74
75fn impl_json_type_macro(ast: &DeriveInput) -> Result<TokenStream, Error> {
76 let name = &ast.ident;
77 let json_fields = fields_to_json(&ast.data, &ast.span())?;
78
79 let format_json = format!("{{\"name\": {}, fields: {}}}", name.to_string(), json_fields);
80 let json = LitStr::new(&*format_json, Span::call_site());
81
82 let gen = quote! {
83 impl json_type::JsonType for #name {
84 fn json_type() -> &'static str {
85 #json
86 }
87 }
88 };
89 Ok(gen.into())
90}
91
92
93#[proc_macro_derive(JsonType)]
94pub fn json_type_derive(input: TokenStream) -> TokenStream {
95 match syn::parse(input) {
96 Ok(ast) => {
97 impl_json_type_macro(&ast)
98 .unwrap_or_else(|e| e.to_compile_error().into())
99 },
100 Err(e) => {
101 e.to_compile_error().into()
102 }
103 }
104}
105
106