json_type_derive/
lib.rs

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