liquid_derive/
object_view.rs

1use proc_macro2::{Ident, TokenStream};
2use quote::quote;
3use syn::{Data, DeriveInput, Error, Fields, Result};
4
5pub(crate) fn derive(input: &DeriveInput) -> TokenStream {
6    let DeriveInput {
7        ident,
8        data,
9        generics,
10        ..
11    } = input;
12
13    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
14
15    let fields = match get_fields(data) {
16        Ok(fields) => fields,
17        Err(err) => return err.to_compile_error(),
18    };
19    let num_fields = fields.len();
20
21    quote! {
22        impl #impl_generics ::liquid::ObjectView for #ident #ty_generics #where_clause {
23            fn as_value(&self) -> &dyn ::liquid::ValueView {
24                self
25            }
26
27            fn size(&self) -> i64 {
28                #num_fields as i64
29            }
30
31            fn keys<'liquid_derive_k>(&'liquid_derive_k self) -> Box<dyn Iterator<Item = ::liquid::model::KStringCow<'liquid_derive_k>> + 'liquid_derive_k> {
32                let mut keys = Vec::with_capacity(#num_fields);
33                #(
34                    keys.push(::liquid::model::KStringCow::from_static(stringify!(#fields)));
35                )*
36                Box::new(keys.into_iter())
37            }
38
39            fn values<'liquid_derive_k>(&'liquid_derive_k self) -> Box<dyn Iterator<Item = &'liquid_derive_k dyn ::liquid::ValueView> + 'liquid_derive_k> {
40                let mut values = Vec::<&dyn ::liquid::ValueView>::with_capacity(#num_fields);
41                #(
42                    values.push(&self.#fields);
43                )*
44                Box::new(values.into_iter())
45            }
46
47            fn iter<'liquid_derive_k>(&'liquid_derive_k self) -> Box<dyn Iterator<Item = (::liquid::model::KStringCow<'liquid_derive_k>, &'liquid_derive_k dyn ::liquid::ValueView)> + 'liquid_derive_k> {
48                let mut values = Vec::<(::liquid::model::KStringCow<'liquid_derive_k>, &'liquid_derive_k dyn ::liquid::ValueView)>::with_capacity(#num_fields);
49                #(
50                    values.push((
51                        ::liquid::model::KStringCow::from_static(stringify!(#fields)),
52                        &self.#fields,
53                    ));
54                )*
55                Box::new(values.into_iter())
56            }
57
58            fn contains_key(&self, index: &str) -> bool {
59                match index {
60                    #(
61                        stringify!(#fields) => true,
62                    )*
63                    _ => false,
64                }
65            }
66
67            fn get<'liquid_derive_s>(&'liquid_derive_s self, index: &str) -> Option<&'liquid_derive_s dyn ::liquid::ValueView> {
68                match index {
69                    #(
70                        stringify!(#fields) => Some(&self.#fields),
71                    )*
72                    _ => None,
73                }
74            }
75        }
76    }
77}
78
79pub(crate) fn core_derive(input: &DeriveInput) -> TokenStream {
80    let DeriveInput {
81        ident,
82        data,
83        generics,
84        ..
85    } = input;
86
87    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
88
89    let fields = match get_fields(data) {
90        Ok(fields) => fields,
91        Err(err) => return err.to_compile_error(),
92    };
93    let num_fields = fields.len();
94
95    quote! {
96        impl #impl_generics ::liquid_core::ObjectView for #ident #ty_generics #where_clause {
97            fn as_value(&self) -> &dyn ::liquid_core::ValueView {
98                self
99            }
100
101            fn size(&self) -> i64 {
102                #num_fields as i64
103            }
104
105            fn keys<'liquid_derive_k>(&'liquid_derive_k self) -> Box<dyn Iterator<Item = ::liquid_core::model::KStringCow<'liquid_derive_k>> + 'liquid_derive_k> {
106                let mut keys = Vec::with_capacity(#num_fields);
107                #(
108                    keys.push(::liquid_core::model::KStringCow::from_static(stringify!(#fields)));
109                )*
110                Box::new(keys.into_iter())
111            }
112
113            fn values<'liquid_derive_k>(&'liquid_derive_k self) -> Box<dyn Iterator<Item = &'liquid_derive_k dyn ::liquid_core::ValueView> + 'liquid_derive_k> {
114                let mut values = Vec::<&dyn ::liquid_core::ValueView>::with_capacity(#num_fields);
115                #(
116                    values.push(&self.#fields);
117                )*
118                Box::new(values.into_iter())
119            }
120
121            fn iter<'liquid_derive_k>(&'liquid_derive_k self) -> Box<dyn Iterator<Item = (::liquid_core::model::KStringCow<'liquid_derive_k>, &'liquid_derive_k dyn ::liquid_core::ValueView)> + 'liquid_derive_k> {
122                let mut values = Vec::<(::liquid_core::model::KStringCow<'liquid_derive_k>, &'liquid_derive_k dyn ::liquid_core::ValueView)>::with_capacity(#num_fields);
123                #(
124                    values.push((
125                        ::liquid_core::model::KStringCow::from_static(stringify!(#fields)),
126                        &self.#fields,
127                    ));
128                )*
129                Box::new(values.into_iter())
130            }
131
132            fn contains_key(&self, index: &str) -> bool {
133                match index {
134                    #(
135                        stringify!(#fields) => true,
136                    )*
137                    _ => false,
138                }
139            }
140
141            fn get<'liquid_derive_s>(&'liquid_derive_s self, index: &str) -> Option<&'liquid_derive_s dyn ::liquid_core::ValueView> {
142                match index {
143                    #(
144                        stringify!(#fields) => Some(&self.#fields),
145                    )*
146                    _ => None,
147                }
148            }
149        }
150    }
151}
152
153pub(crate) fn get_fields(data: &Data) -> Result<Vec<&Ident>> {
154    let fields = match data {
155        Data::Struct(data) => &data.fields,
156        Data::Enum(data) => {
157            return Err(Error::new_spanned(
158                data.enum_token,
159                "`ObjectView` support for `enum` is unimplemented.",
160            ));
161        }
162        Data::Union(data) => {
163            return Err(Error::new_spanned(
164                data.union_token,
165                "Unions cannot impl ObjectView.",
166            ));
167        }
168    };
169
170    let fields = match fields {
171        Fields::Named(fields) => fields,
172        Fields::Unnamed(fields) => {
173            return Err(Error::new_spanned(
174                fields,
175                "`ObjectView` support for tuple-structs is unimplemented.",
176            ))
177        }
178        Fields::Unit => {
179            return Err(Error::new_spanned(
180                fields,
181                "`ObjectView` support for unit-structs is unimplemented.",
182            ))
183        }
184    };
185
186    Ok(fields
187        .named
188        .iter()
189        .map(|field| field.ident.as_ref().expect("Fields are named."))
190        .collect())
191}