liquid_derive/
object_view.rs1use 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}