Skip to main content

tablo_macros/
lib.rs

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