quokka_admin_macros/
lib.rs

1// Derive AdminCreateForm<S>
2// Derive AdminUpdateForm<S>
3// Derive AdminListing
4
5use syn::parse_macro_input;
6
7mod derive;
8
9///
10/// Derive the AdminCreateForm trait
11///
12/// # Attributes
13///
14/// - `create_form` - struct attribute, required
15///   - `entity_name` - name of the entity (database table), required
16///   - `create_query` - A handler which persists the entity, optional, defaults to a Postgres query constructed by the macro
17/// - `create_field` - field attribute, optional
18///   - `required` - optional, Make the field is required (only used in the frontend, losening this will require the `Option<T>` type in the struct)
19///   - `default` - optional, The default value for the entity
20///   - `label` - optional, The label for the input
21///   - `form_field` - optional, The form field type which is rendered in the frontend
22///
23/// # Example
24///
25/// This example shows all available attributes and values
26///
27/// // TODO: Create a test for it
28///
29/// ```raw
30/// #[derive(Clone, Debug, AdminCreateForm, serde::Deserialize, serde::Serialize, sqlx::FromRow)]
31/// #[create_form(entity_name = "test", create_query = create_entity(state).await)]
32/// pub struct TestEntityCreateForm {
33///     #[create_field(
34///         required,                             // Whether the field is required or not
35///         default = "Test Entity",              // The default value for the entity
36///         label = "Entity Name",                // The label for the input
37///         form_field = CustomNameSelect::new(), // The form field type which is rendered in the frontend
38///     )]
39///     name: String,
40/// }
41///
42/// async fn create_entity<S: quokka::state::State + quokka::state::ProvideState<Database>>(state: &S) -> quokka::Result<()> {
43///     // Persist the entity here
44///
45///     Ok(())
46/// }
47/// ```
48///
49#[proc_macro_derive(AdminCreateForm, attributes(create_form, create_field))]
50pub fn derive_admin_create_form(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
51    derive::admin_create_form::derive_output(parse_macro_input!(input as syn::DeriveInput)).into()
52}
53
54///
55/// Derive the AdminUpdateForm trait
56///
57/// # Attributes
58///
59/// - `update_form` - struct attribute, required
60///   - `primary_keys` - the type of the primary key(s), required if no primary key is indicated in the fields
61///   - `entity_name` - name of the entity (database table), required
62///   - `update_query` - A handler which persists the entity, optional, defaults to a Postgres query constructed by the macro
63/// - `update_field` - field attribute, optional
64///   - `required` - optional, Make the field is required (only used in the frontend, losening this will require the `Option<T>` type in the struct)
65///   - `default` - optional, The default value for the entity
66///   - `label` - optional, The label for the input
67///   - `form_field` - optional, The form field type which is rendered in the frontend
68///   - `primary_key` - optional, Marking the field as primary key, will be used for the automatic update query and makes the field type `HiddenField` by default
69///
70/// # Example
71///
72/// This example shows all available attributes and values
73///
74/// // TODO: Create a test for it
75///
76/// ```raw
77/// #[derive(Clone, Debug, AdminUpdateForm, serde::Deserialize, serde::Serialize, sqlx::FromRow)]
78/// #[update_form(entity_name = "test", primary_keys = i32, update_query = update_entity(state).await, get_query = get_entity(state).await)]
79/// pub struct TestEntityUpdateForm {
80///     #[update_field(primary_key)]
81///     id: i32,
82///     #[update_field(
83///         required,
84///         default = "Test Entity",
85///         label = "Entity Name",
86///         form_field = CustomNameSelect::new(),
87///     )]
88///     name: String,
89/// }
90///
91/// async fn update_entity<S: quokka::state::State + quokka::state::ProvideState<Database>>(state: &S) -> quokka::Result<()> {
92///     // Update the entity here
93///
94///     Ok(())
95/// }
96///
97/// async fn get_entity<S: quokka::state::State + quokka::state::ProvideState<Database>>() -> quokka::Result<TestEntityUpdateForm> {
98///     todo!("Implement getting the entity")
99/// }
100/// ```
101///
102#[proc_macro_derive(AdminUpdateForm, attributes(update_form, update_field))]
103pub fn derive_admin_update_form(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
104    derive::admin_update_form::derive_output(parse_macro_input!(input as syn::DeriveInput)).into()
105}
106
107///
108/// Derive the AdminListing trait
109///
110/// # Attributes
111///
112/// - `listing` struct attribute, required
113///   - `entity_name` required, The name of the entity (database table)
114///   - `get_query` optional, The function which queries the entities, default utilizes a search using the `PaginatedSearch`
115///   - `get_state` optional, The state on which the query is executed on, default `PaginatedSearch`
116///   - `entity` optional, The entity type, default self
117/// - `listing_field` field attribute, optional
118///   - `field` optional, The type which is used to display the value in the frontend
119///
120/// # Example
121///
122/// // TODO: Create a test for it
123///
124/// ```raw
125/// #[derive(Clone, Debug, AdminListing, serde::Deserialize, serde::Serialize)]
126/// #[listing(entity_name = "test", get_query = get_test_entities(state).await, get_state = quokka::state::Database), entity = Test]
127/// pub struct TestEntityListing {
128///     id: i32,
129///     name: String,
130/// }
131///
132/// async fn get_test_entities(state: TestEntityListing::State) -> quokka::Result<quokka::helper::database::PaginatedResult<TestEntityListing::Entity>> {
133///     todo!("Do your query here")
134/// }
135/// ```
136///
137#[proc_macro_derive(AdminListing, attributes(listing, listing_field))]
138pub fn derive_admin_listing(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
139    derive::admin_listing::derive_output(parse_macro_input!(input as syn::DeriveInput)).into()
140}