turso_orm_macros/lib.rs
1//! Macros: the derive macros that generate entity, relation, identifier and row-decoding impls for turso-orm.
2//!
3//! The macros are re-exported by `turso_orm` behind its `macros` feature
4//! and by `turso_orm_migration`; they are not meant to be used from this
5//! crate directly, because the code they expand to refers to
6//! `::turso_orm::...` paths and to the `__private` module that crate
7//! exposes for generated code only.
8//!
9//! Each derive lives in its own module with a single `expand` function that
10//! takes the parsed input and returns either the generated tokens or a
11//! `syn::Error` carrying the span of the offending item, so that users see
12//! the error on their own code rather than inside the macro. Attribute
13//! parsing is shared through the `attrs` module.
14//!
15//! - `DeriveEntityModel` expands a `Model` struct into `Entity`, `Column`,
16//! `PrimaryKey` and `ActiveModel` plus their trait impls.
17//! - `DeriveRelation` expands a `Relation` enum into `RelationTrait` and
18//! `Related<R>` impls, junction tables included.
19//! - `DeriveActiveEnum` makes a fieldless enum a column type.
20//! - `DerivePartialModel` makes a struct a self-selecting projection.
21//! - `DeriveIntoActiveModel` converts a plain struct into an active model.
22//! - `DeriveIden` implements the identifier traits on a unit struct or a
23//! fieldless enum.
24//! - `FromQueryResult` implements row decoding for a plain struct.
25//! - `DeriveMigrationName` names a migration after its module.
26
27#![cfg_attr(docsrs, feature(doc_cfg))]
28#![allow(
29 clippy::needless_pass_by_value,
30 clippy::too_many_lines,
31 reason = "proc-macro expansion code is naturally long and passes syn trees by value"
32)]
33
34mod active_enum;
35mod attrs;
36mod entity;
37mod from_query_result;
38mod iden;
39mod into_active_model;
40mod partial_model;
41mod relation;
42
43use proc_macro::TokenStream;
44
45/// Derives an entity from a `Model` struct.
46///
47/// Generates `Entity`, `Column`, `PrimaryKey` and `ActiveModel` next to the
48/// model. Field attributes: `#[turso(primary_key)]`,
49/// `#[turso(auto_increment = false)]`, `#[turso(column_name = "...")]`,
50/// `#[turso(unique)]`, `#[turso(indexed)]`, `#[turso(default_value = ...)]`,
51/// `#[turso(ignore)]`. Struct attribute: `#[turso(table_name = "...")]`.
52#[proc_macro_derive(DeriveEntityModel, attributes(turso))]
53pub fn derive_entity_model(input: TokenStream) -> TokenStream {
54 let input = syn::parse_macro_input!(input as syn::DeriveInput);
55 entity::expand(input)
56 .unwrap_or_else(syn::Error::into_compile_error)
57 .into()
58}
59
60/// Derives `RelationTrait` and `Related<R>` impls from a `Relation` enum.
61///
62/// Variant attributes: `#[turso(belongs_to = "super::user::Entity", from = "Column::UserId", to = "super::user::Column::Id")]`,
63/// `#[turso(has_many = "super::post::Entity")]`, `#[turso(has_one = "...")]`,
64/// `#[turso(has_many = "super::tag::Entity", via = "super::post_tag::Entity")]`
65/// for a many-to-many relation, plus optional `on_delete = "Cascade"` /
66/// `on_update = "..."` / `skip_fk`. The first variant naming a target
67/// provides the `Related<Target>` impl; further variants to the same target
68/// are used through their `def()`.
69#[proc_macro_derive(DeriveRelation, attributes(turso))]
70pub fn derive_relation(input: TokenStream) -> TokenStream {
71 let input = syn::parse_macro_input!(input as syn::DeriveInput);
72 relation::expand(input)
73 .unwrap_or_else(syn::Error::into_compile_error)
74 .into()
75}
76
77/// Derives `IdenStatic` and the SQL identifier traits for a unit struct or
78/// a fieldless enum.
79///
80/// Names are `snake_case` unless overridden with `#[turso(iden = "...")]`.
81#[proc_macro_derive(DeriveIden, attributes(turso))]
82pub fn derive_iden(input: TokenStream) -> TokenStream {
83 let input = syn::parse_macro_input!(input as syn::DeriveInput);
84 iden::expand(input)
85 .unwrap_or_else(syn::Error::into_compile_error)
86 .into()
87}
88
89/// Derives `FromQueryResult` for a plain struct.
90///
91/// Every field is read from the column of the same name, or from the one
92/// given with `#[turso(column_name = "...")]`.
93#[proc_macro_derive(FromQueryResult, attributes(turso))]
94pub fn derive_from_query_result(input: TokenStream) -> TokenStream {
95 let input = syn::parse_macro_input!(input as syn::DeriveInput);
96 from_query_result::expand(input)
97 .unwrap_or_else(syn::Error::into_compile_error)
98 .into()
99}
100
101/// Derives `ActiveEnum` and the column-type impls for a fieldless enum.
102///
103/// Struct attribute: `#[turso(rs_type = "String")]` or an integer type such
104/// as `"i32"`. Variant attributes: `#[turso(string_value = "...")]`, which
105/// defaults to the `snake_case` variant name, or `#[turso(num_value = 1)]`,
106/// which is required for integer-backed enums.
107#[proc_macro_derive(DeriveActiveEnum, attributes(turso))]
108pub fn derive_active_enum(input: TokenStream) -> TokenStream {
109 let input = syn::parse_macro_input!(input as syn::DeriveInput);
110 active_enum::expand(input)
111 .unwrap_or_else(syn::Error::into_compile_error)
112 .into()
113}
114
115/// Derives `PartialModelTrait` and `FromQueryResult` for a projection struct.
116///
117/// Struct attribute: `#[turso(entity = "path::Entity")]`, required. Field
118/// attributes: `#[turso(from_col = "Variant")]` to read another column than
119/// the one named after the field, `#[turso(from_expr = "...")]` to read an
120/// expression written in Rust against `Expr`.
121#[proc_macro_derive(DerivePartialModel, attributes(turso))]
122pub fn derive_partial_model(input: TokenStream) -> TokenStream {
123 let input = syn::parse_macro_input!(input as syn::DeriveInput);
124 partial_model::expand(input)
125 .unwrap_or_else(syn::Error::into_compile_error)
126 .into()
127}
128
129/// Derives `IntoActiveModel` for a plain struct whose fields are a subset
130/// of an entity's columns.
131///
132/// Struct attribute: `#[turso(active_model = "path::ActiveModel")]`, the
133/// `ActiveModel` in scope by default. Field attribute: `#[turso(ignore)]`.
134/// A plain field becomes `Set`; an `Option` around the attribute type
135/// becomes `Set` when `Some` and `NotSet` when `None`.
136#[proc_macro_derive(DeriveIntoActiveModel, attributes(turso))]
137pub fn derive_into_active_model(input: TokenStream) -> TokenStream {
138 let input = syn::parse_macro_input!(input as syn::DeriveInput);
139 into_active_model::expand(input)
140 .unwrap_or_else(syn::Error::into_compile_error)
141 .into()
142}
143
144/// Derives `MigrationName` from the module path.
145///
146/// The name is the last segment of `module_path!()` evaluated where the
147/// derive is written, so a migration in module
148/// `m20240101_000001_create_user` is named exactly that. Deriving it from
149/// the module rather than the type keeps every migration struct free to be
150/// called `Migration`.
151#[proc_macro_derive(DeriveMigrationName)]
152pub fn derive_migration_name(input: TokenStream) -> TokenStream {
153 let input = syn::parse_macro_input!(input as syn::DeriveInput);
154 let ident = input.ident;
155 quote::quote! {
156 impl ::turso_orm_migration::MigrationName for #ident {
157 fn name(&self) -> &str {
158 let path = module_path!();
159 path.rsplit("::").next().unwrap_or(path)
160 }
161 }
162 }
163 .into()
164}