bournemacro/
lib.rs

1use syn::parse::discouraged::Speculative;
2use syn::parse::*;
3use syn::parse_macro_input;
4
5enum Value {
6    Null,
7    Object(Vec<KeyValuePair>),
8    Array(Vec<Value>),
9    Expr(syn::Expr),
10}
11
12struct Object(Vec<KeyValuePair>);
13
14impl Parse for Object {
15    fn parse(input: ParseStream) -> Result<Self> {
16        let content;
17        syn::braced!(content in input);
18        let tokens = content.parse_terminated(KeyValuePair::parse, syn::Token![,])?;
19        let pairs = tokens.into_iter().collect();
20        Ok(Object(pairs))
21    }
22}
23
24struct Array(Vec<Value>);
25
26impl Parse for Array {
27    fn parse(input: ParseStream) -> Result<Self> {
28        let content;
29        syn::bracketed!(content in input);
30        let tokens = content.parse_terminated(Value::parse, syn::Token![,])?;
31        let values = tokens.into_iter().collect();
32        Ok(Array(values))
33    }
34}
35
36struct Null;
37
38impl Parse for Null {
39    fn parse(input: ParseStream) -> Result<Self> {
40        let fork = input.fork();
41        if let Ok(kw) = fork.parse::<syn::Ident>() {
42            if kw == "null" {
43                input.advance_to(&fork);
44                return Ok(Null);
45            }
46        }
47        Err(syn::Error::new(input.span(), "expected `null`"))
48    }
49}
50
51impl Parse for Value {
52    fn parse(input: ParseStream) -> syn::Result<Self> {
53        if let Ok(_) = input.parse::<Null>() {
54            Ok(Value::Null)
55        } else if let Ok(Array(array)) = input.parse::<Array>() {
56            Ok(Value::Array(array))
57        } else if let Ok(Object(object)) = input.parse::<Object>() {
58            Ok(Value::Object(object))
59        } else if let Ok(expr) = input.parse::<syn::Expr>() {
60            Ok(Value::Expr(expr))
61        } else {
62            Err(syn::Error::new(input.span(), "Unexpected token."))
63        }
64    }
65}
66
67struct KeyValuePair {
68    key: syn::Expr,
69    value: Value,
70}
71
72impl Parse for KeyValuePair {
73    fn parse(input: ParseStream) -> Result<Self> {
74        let key = input.parse::<syn::Expr>()?;
75        input.parse::<syn::Token![:]>()?;
76        let value = input.parse::<Value>()?;
77        Ok(KeyValuePair { key, value })
78    }
79}
80
81impl Value {
82    fn to_tokenstream(self) -> proc_macro2::TokenStream {
83        use quote::quote;
84        match self {
85            Value::Null => quote!(bourne::Value::Null),
86            Value::Object(object) => {
87                let capacity = object.len();
88                let inserts = object.into_iter().map(|KeyValuePair { key, value }| {
89                    let value = value.to_tokenstream();
90                    quote! { map.insert((#key).to_owned(), #value); }
91                }).collect::<Vec<_>>();
92                quote! {
93                    {
94                        let mut map = bourne::ValueMap::with_capacity(#capacity);
95                        #(#inserts)*
96                        bourne::Value::Object(map)
97                    }
98                }
99            },
100            Value::Array(array) => {
101                let capacity = array.len();
102                let lines = array.into_iter().map(|value| {
103                    let value = value.to_tokenstream();
104                    quote!{ array.push(#value); }
105                }).collect::<Vec<_>>();
106                quote! {
107                    {
108                        let mut array = Vec::<bourne::Value>::with_capacity(#capacity);
109                        #(#lines)*
110                        bourne::Value::Array(array)
111                    }
112                }
113            },
114            Value::Expr(expr) => {
115                quote!{ bourne::Value::from(#expr) }
116            },
117        }
118    }
119}
120
121/// Create a JSON object. Expressions are allowed as values as long as the result is convertible to a Value.
122/// Example:
123/// ```rust,no_run
124/// let number = 3.14;
125/// let value = json!(
126///     {
127///         "number" : number
128///     }
129/// );
130/// ```
131#[proc_macro]
132pub fn json(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
133    if input.is_empty() {
134        quote::quote!{ bourne::Value::Null }.into()
135    } else {
136        let value = parse_macro_input!(input as Value);
137        value.to_tokenstream().into()
138    }
139}