lorm_macros/
lib.rs

1use proc_macro::TokenStream;
2use syn::{DeriveInput, parse_macro_input};
3
4mod models;
5mod orm;
6mod utils;
7
8/// `#[derive(ToLOrm)]`
9/// generate methods for Object Relational Mapping.
10///
11/// attributes:
12///
13/// `#[lorm(pk)]`
14///  Annotated field is marked as being the primary key and can only be generated at insertion time.
15///  This field is also automatically considered as a `#[lorm(by)]` field.
16///  - If `#[lorm(new)]` is specified, it will use its struct method to generate a new pk at insertion time
17///  - If `#[lorm(is_set)]` is specified, it will use its instance method against `self` to check if the pk is set. Otherwise it compares the pk value with its <struct>::default() (assuming the Default trait is set)
18///  - If `#[lorm(readonly)]` is specified, it will ignore is_set `#[lorm(new)]` and `#[lorm(is_set)]` and let the database handles the field
19///
20/// `#[lorm(rename="name")]`
21///   - at struct level to rename at table name
22///   - at field level to rename at column name
23///
24///   by default, a table name is the struct name pluralized and converted to table case: UserDetail => user_details.
25///   by default, a field name is converted to snake_case: UserDetail => user_detail.
26///
27/// `#[lorm(skip)]`
28///  Ignore field for persistence operations. Using sqlx::FromRow, skip needs `#[lorm(skip)]` and `#[sqlx(skip)]`
29///
30/// `#[lorm(readonly)]`
31///  readonly attribute. Cannot be updated not inserted.
32///  Special cases to consider:
33///   - If applied to the primary key, key generation is left to the database. No update possible as it is the primary key.
34///   - If applied to create_at or updated_at field, timestamp generation is left to the database. No update possible.
35///
36/// `#[lorm(by)]`
37///  Generates query methods for this field:
38///  - `by_<field>(executor, value)` - Find single record by field value
39///  - `with_<field>(executor, value)` - Find all records matching field value
40///  - `where_<field>(Where, value)` - Filter in select() query builder
41///  - `where_between_<field>(start, end)` - Range filter in select() query builder
42///  - `order_by_<field>()` - Order results by this field (chain with `.asc()` or `.desc()`)
43///  - `group_by_<field>()` - Group results by this field
44///
45/// `#[lorm(created_at)]`
46///  Add the `#[lorm(created_at)]` annotation to mark the field as the `created_at` field.
47///  - If `#[lorm(new)]` is specified, it will use its method to update the time upon insertion
48///  - If `#[lorm(readonly)]` is specified, it will ignore is_set `#[lorm(new)]` and let the database handles the field
49///
50/// `#[lorm(updated_at)]`
51///  Add the `#[lorm(updated_at)]` annotation to mark the field as the `updated_at` field.
52///  - If `#[lorm(new)]` is specified, it will use its method to update the time upon insertion and update
53///  - If `#[lorm(readonly)]` is specified, it will ignore is_set `#[lorm(new)]` and let the database handles the field
54///
55/// `#[lorm(new="module::path::class::new_custom()")]`
56///  Add the `#[lorm(new="module::path::class::new_custom()")]` annotation to use a custom creation method.
57///  - The function call is expected to return an instance
58///  - When not provided, the type::new() method is called
59///
60/// `#[lorm(is_set="is_nil()")]`
61///  Uses a specific function call to check if the returned value if the default value.
62///  The function call is expected to return bool.
63///  Defaults to class_type::default() which assumes both the Default and PartialEq trait are implemented.
64///
65#[proc_macro_derive(ToLOrm,
66    attributes(
67        lorm,
68        // lorm(pk),
69        // lorm(by),
70        // lorm(skip),
71        // lorm(readonly),
72        // lorm(new="module::path::class::new_custom()"),
73        // lorm(is_set="is_nil()"),
74        // lorm(rename="name"),
75        // lorm(created_at),
76        // lorm(updated_at),
77    )
78)]
79pub fn sql_derive_to_orm(input: TokenStream) -> TokenStream {
80    let input = parse_macro_input!(input as DeriveInput);
81    match orm::expand_derive_to_orm(&input) {
82        Ok(ts) => ts,
83        Err(e) => e.to_compile_error().into(),
84    }
85}