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}