Skip to main content

tablo_macros/
lib.rs

1//! The `ModelColumns`, `RecordForm`, `ModelForm`, `EmbeddedForm`, `ActionInput`, `RepeaterItem`
2//! and `Options` derives, re-exported by `tablo-core`.
3
4mod embedded;
5mod fields;
6mod input;
7mod model_columns;
8mod model_form;
9mod options;
10mod record_form;
11
12use proc_macro::TokenStream;
13use syn::DeriveInput;
14
15/// Derives `EmbeddedForm` for an embedded struct or enum; documented on the `tablo-core` re-export.
16#[proc_macro_derive(EmbeddedForm, attributes(form))]
17pub fn embedded_form(input: TokenStream) -> TokenStream {
18    let input = syn::parse_macro_input!(input as DeriveInput);
19    embedded::expand(input)
20}
21
22/// Derive `RecordForm` for the typed value a resource's form writes; documented on the `tablo-core`
23/// re-export.
24#[proc_macro_derive(RecordForm, attributes(form))]
25pub fn record_form(input: TokenStream) -> TokenStream {
26    let input = syn::parse_macro_input!(input as DeriveInput);
27    record_form::expand_tokens(input).into()
28}
29
30/// Derive a model's default table and detail page; documented on the `tablo-core` re-export.
31#[proc_macro_derive(ModelColumns, attributes(columns))]
32pub fn model_columns(input: TokenStream) -> TokenStream {
33    let input = syn::parse_macro_input!(input as DeriveInput);
34    model_columns::expand_tokens(input).into()
35}
36
37/// Derive the `RecordForm` struct of a model's fillable fields; documented on the `tablo-core`
38/// re-export.
39#[proc_macro_derive(ModelForm, attributes(form))]
40pub fn model_form(input: TokenStream) -> TokenStream {
41    let input = syn::parse_macro_input!(input as DeriveInput);
42    model_form::expand_tokens(input).into()
43}
44
45/// Derive `ActionInput` for the typed value an action asks for; documented on the `tablo-core`
46/// re-export.
47#[proc_macro_derive(ActionInput, attributes(form))]
48pub fn action_input(input: TokenStream) -> TokenStream {
49    let input = syn::parse_macro_input!(input as DeriveInput);
50    input::expand_tokens(input, input::Target::Input).into()
51}
52
53/// Derive `RepeaterItem` for one item of a repeater; documented on the `tablo-core` re-export.
54#[proc_macro_derive(RepeaterItem, attributes(form))]
55pub fn repeater_item(input: TokenStream) -> TokenStream {
56    let input = syn::parse_macro_input!(input as DeriveInput);
57    input::expand_tokens(input, input::Target::Item).into()
58}
59
60/// Derive `Options` for a unit-variant enum; documented on the `tablo-core` re-export.
61#[proc_macro_derive(Options, attributes(option))]
62pub fn options(input: TokenStream) -> TokenStream {
63    let input = syn::parse_macro_input!(input as DeriveInput);
64    options::expand_tokens(input).into()
65}
66
67/// The path the generated code names `tablo-core` by: the `tablo` facade, which
68/// re-exports it at its root, else `::tablo_core`, either under the consumer's
69/// rename. The facade comes first: it is what an app depends on, and it
70/// reaches every path the generated code names.
71fn tablo_core_path(span: &syn::Ident, derive: &str) -> syn::Result<proc_macro2::TokenStream> {
72    let found = proc_macro_crate::crate_name("tablo")
73        .map(|found| (found, "tablo"))
74        .or_else(|_| proc_macro_crate::crate_name("tablo-core").map(|found| (found, "tablo_core")));
75    match found {
76        Ok((found, own_name)) => {
77            let name = match found {
78                proc_macro_crate::FoundCrate::Itself => own_name.to_string(),
79                proc_macro_crate::FoundCrate::Name(n) => n,
80            };
81            let ident = syn::Ident::new(&name.replace('-', "_"), proc_macro2::Span::call_site());
82            Ok(quote::quote! { ::#ident })
83        }
84        Err(_) => Err(syn::Error::new_spanned(
85            span,
86            format!("`tablo` (or `tablo-core`) must be a dependency to #[derive({derive})]"),
87        )),
88    }
89}