1#![warn(missing_docs)]
5
6use proc_macro::TokenStream;
7use quote::quote;
8use syn::{Data, DeriveInput, Expr, ExprLit, Fields, ItemFn, LitStr};
9
10fn with_serde(mut input: DeriveInput, default: bool) -> TokenStream {
11 let derives = if default {
12 quote!(rango::prelude::Deserialize, Default)
13 } else {
14 quote!(rango::prelude::Deserialize)
15 };
16 input.attrs.push(syn::parse_quote!(#[derive(#derives)]));
17 input
18 .attrs
19 .push(syn::parse_quote!(#[serde(crate = "rango::serde")]));
20 quote!(#input).into()
21}
22
23#[proc_macro_attribute]
27pub fn form(_attr: TokenStream, item: TokenStream) -> TokenStream {
28 let input = syn::parse_macro_input!(item as DeriveInput);
29 with_serde(input, true)
30}
31
32#[proc_macro_attribute]
36pub fn input(_attr: TokenStream, item: TokenStream) -> TokenStream {
37 let input = syn::parse_macro_input!(item as DeriveInput);
38 with_serde(input, false)
39}
40
41#[proc_macro_attribute]
45pub fn main(_attr: TokenStream, item: TokenStream) -> TokenStream {
46 let func = syn::parse_macro_input!(item as ItemFn);
47 if func.sig.asyncness.is_none() {
48 return syn::Error::new_spanned(&func.sig, "expected `async fn main`")
49 .to_compile_error()
50 .into();
51 }
52 let output = &func.sig.output;
53 let body = &func.block;
54 let attrs = &func.attrs;
55 let vis = &func.vis;
56 quote! {
57 #(#attrs)*
58 #vis fn main() #output {
59 rango::tokio::runtime::Builder::new_multi_thread()
60 .enable_all()
61 .build()
62 .expect("failed to build tokio runtime")
63 .block_on(async #body)
64 }
65 }
66 .into()
67}
68
69#[proc_macro_attribute]
73pub fn template(attr: TokenStream, item: TokenStream) -> TokenStream {
74 let attr: proc_macro2::TokenStream = attr.into();
75 let mut input = syn::parse_macro_input!(item as DeriveInput);
76 input
77 .attrs
78 .push(syn::parse_quote!(#[derive(rango::prelude::Template)]));
79 input
80 .attrs
81 .push(syn::parse_quote!(#[template(#attr, askama = rango::askama)]));
82 quote!(#input).into()
83}
84
85#[proc_macro_derive(Model, attributes(model, key, references, via, default))]
92pub fn derive_model(input: TokenStream) -> TokenStream {
93 let input = syn::parse_macro_input!(input as DeriveInput);
94 match expand(&input) {
95 Ok(ts) => ts.into(),
96 Err(e) => e.to_compile_error().into(),
97 }
98}
99
100fn expand(input: &DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
101 let name = &input.ident;
102 let (table, deeds) = parse_model(input)?;
103
104 let fields = match &input.data {
105 Data::Struct(data) => match &data.fields {
106 Fields::Named(fields) => &fields.named,
107 _ => return Err(syn::Error::new_spanned(input, "only named fields")),
108 },
109 _ => return Err(syn::Error::new_spanned(input, "only structs")),
110 };
111
112 let mut field_defs = Vec::new();
113 let mut write_parts = Vec::new();
114 let mut read_parts = Vec::new();
115 let mut key_ident = None;
116
117 for field in fields.into_iter() {
118 let ident = field.ident.as_ref().unwrap();
119 let attrs = &field.attrs;
120 let ty = &field.ty;
121
122 let is_key = attrs.iter().any(|a| a.path().is_ident("key"));
123 let references = parse_references(attrs)?;
124 let references = references.as_deref();
125 let via = parse_via(attrs)?;
126 let default = parse_default(attrs)?;
127
128 let (field_type, is_optional) = unpack_option(ty);
129 let (cell_type, is_many) = unpack_vec(field_type);
130 if is_many && is_optional {
131 return Err(syn::Error::new_spanned(
132 field,
133 "optional lists need plain Vec",
134 ));
135 }
136 if is_many && is_key {
137 return Err(syn::Error::new_spanned(field, "key needs a single value"));
138 }
139 let name = ident.to_string();
140 let is_id = is_key && name == "id" && !is_many;
141
142 let field_def = if is_id {
143 quote! { rango::Field::id() }
144 } else if is_key {
145 let mut def = quote! { rango::Field::key::<#cell_type>(#name) };
146 if let Some(ref_path) = references {
147 def = quote! { #def.references(#ref_path) };
148 }
149 def
150 } else if is_many {
151 let mut def = quote! { rango::Field::many(#name) };
152 if let Some((through, mine, theirs)) = via {
153 def = quote! {
154 #def.link(rango::Link::Via(
155 rango::Table(#through),
156 rango::Name(#mine),
157 rango::Name(#theirs)
158 ))
159 };
160 }
161 def
162 } else {
163 let mut def = quote! { rango::Field::cell::<#cell_type>(#name) };
164 if is_optional {
165 def = quote! { #def.optional() };
166 }
167 if let Some(ref_path) = references {
168 def = quote! { #def.references(#ref_path) };
169 }
170 if let Some(default_val) = default {
171 def = quote! { #def.default_value(#default_val) };
172 }
173 def
174 };
175
176 field_defs.push(field_def);
177
178 if is_many {
179 read_parts.push(quote! { #ident: Vec::new() });
180 continue;
181 }
182 if is_key {
183 key_ident = Some(ident);
184 }
185 if !is_id {
186 write_parts.push(quote! { rango::Storable::put(&self.#ident, w); });
187 }
188 read_parts.push(quote! { #ident: rango::Storable::take(r)? });
189 }
190
191 let write_id = match &key_ident {
192 Some(ident) => quote! { rango::Storable::put(&self.#ident, w); },
193 None => quote! { w.nothing(); },
194 };
195 let read_id = match &key_ident {
196 Some(ident) => quote! {
197 self.#ident = rango::Storable::take(r)?;
198 Ok(())
199 },
200 None => quote! { Ok(()) },
201 };
202 let actions = if deeds.is_empty() {
203 quote! {}
204 } else {
205 quote! {
206 fn actions() -> Vec<rango::Action> {
207 vec![#(#deeds()),*, rango::Action::wipe()]
208 }
209 }
210 };
211
212 Ok(quote! {
213 impl rango::Model for #name {
214 fn table() -> rango::Table {
215 rango::Table(#table)
216 }
217
218 fn fields() -> Vec<rango::Field> {
219 vec![#(#field_defs),*]
220 }
221
222 fn write(&self, w: &mut dyn rango::Writer) {
223 #(#write_parts)*
224 }
225
226 fn read(r: &mut dyn rango::Reader) -> Result<Self, rango::StoreError> {
227 Ok(Self {
228 #(#read_parts),*
229 })
230 }
231
232 fn write_id(&self, w: &mut dyn rango::Writer) {
233 #write_id
234 }
235
236 fn read_id(&mut self, r: &mut dyn rango::Reader) -> Result<(), rango::StoreError> {
237 #read_id
238 }
239
240 #actions
241 }
242 })
243}
244
245fn parse_model(input: &DeriveInput) -> syn::Result<(String, Vec<syn::Path>)> {
246 let mut table = None;
247 let mut deeds = Vec::new();
248 for attr in &input.attrs {
249 if !attr.path().is_ident("model") {
250 continue;
251 }
252 attr.parse_nested_meta(|meta| {
253 if meta.path.is_ident("table") {
254 let value = meta.value()?;
255 let lit: LitStr = value.parse()?;
256 table = Some(lit.value());
257 Ok(())
258 } else if meta.path.is_ident("actions") {
259 let value = meta.value()?;
260 let lit: LitStr = value.parse()?;
261 for raw in lit.value().split(',') {
262 let raw = raw.trim();
263 if raw.is_empty() {
264 continue;
265 }
266 deeds.push(
267 syn::parse_str::<syn::Path>(raw)
268 .map_err(|_| meta.error("expected action paths"))?,
269 );
270 }
271 Ok(())
272 } else {
273 Err(meta.error("expected `table = \"...\"` or `actions = \"...\"`"))
274 }
275 })?;
276 }
277 let name = input.ident.to_string().to_lowercase();
278 Ok((table.unwrap_or(format!("{name}s")), deeds))
279}
280
281fn parse_references(attrs: &[syn::Attribute]) -> syn::Result<Option<String>> {
282 for attr in attrs {
283 if !attr.path().is_ident("references") {
284 continue;
285 }
286 let lit: LitStr = attr.parse_args()?;
287 return Ok(Some(lit.value()));
288 }
289 Ok(None)
290}
291
292fn parse_via(attrs: &[syn::Attribute]) -> syn::Result<Option<(String, String, String)>> {
293 for attr in attrs {
294 if !attr.path().is_ident("via") {
295 continue;
296 }
297 let lit: LitStr = attr.parse_args()?;
298 let text = lit.value();
299 let parts: Vec<&str> = text.split('.').collect();
300 if let [through, mine, theirs] = parts.as_slice() {
301 return Ok(Some((
302 through.to_string(),
303 mine.to_string(),
304 theirs.to_string(),
305 )));
306 }
307 return Err(syn::Error::new_spanned(
308 attr,
309 "expected `#[via(\"through.mine.theirs\")]`",
310 ));
311 }
312 Ok(None)
313}
314
315fn parse_default(attrs: &[syn::Attribute]) -> syn::Result<Option<proc_macro2::TokenStream>> {
316 for attr in attrs {
317 if !attr.path().is_ident("default") {
318 continue;
319 }
320 let expr: Expr = attr.parse_args()?;
321 if let Expr::Lit(ExprLit {
322 lit: syn::Lit::Str(s),
323 ..
324 }) = expr
325 {
326 let val = s.value();
327 return Ok(Some(quote! { String::from(#val) }));
328 }
329 return Ok(Some(quote! { #expr }));
330 }
331 Ok(None)
332}
333
334fn unpack_option(ty: &syn::Type) -> (&syn::Type, bool) {
335 if let syn::Type::Path(tp) = ty
336 && let Some(segment) = tp.path.segments.last()
337 && segment.ident == "Option"
338 && let syn::PathArguments::AngleBracketed(args) = &segment.arguments
339 && let Some(syn::GenericArgument::Type(inner)) = args.args.first()
340 {
341 return (inner, true);
342 }
343 (ty, false)
344}
345
346fn unpack_vec(ty: &syn::Type) -> (&syn::Type, bool) {
347 if let syn::Type::Path(tp) = ty
348 && let Some(segment) = tp.path.segments.last()
349 && segment.ident == "Vec"
350 && let syn::PathArguments::AngleBracketed(args) = &segment.arguments
351 && let Some(syn::GenericArgument::Type(inner)) = args.args.first()
352 {
353 return (inner, true);
354 }
355 (ty, false)
356}