Skip to main content

toasty_macros/
lib.rs

1//! Procedural macros for the Toasty ORM.
2//!
3//! This crate provides `#[derive(Model)]`, `#[derive(Embed)]`, and related
4//! attribute macros that generate query builders, schema registration, and
5//! database mapping code.
6
7#![warn(missing_docs)]
8
9extern crate proc_macro;
10
11mod create;
12mod model;
13mod query;
14
15use proc_macro::TokenStream;
16
17/// Derive macro that turns a struct into a Toasty model backed by a database
18/// table.
19///
20/// For a tutorial-style introduction, see the [Toasty guide].
21///
22#[doc = include_str!(concat!(env!("OUT_DIR"), "/guide_link.md"))]
23///
24/// # Overview
25///
26/// Applying `#[derive(Model)]` to a named struct generates:
27///
28/// - A [`Model`] trait implementation, including the associated `Query`,
29///   `Create`, and `Update` builder types.
30/// - A [`Load`] implementation for deserializing rows from the database.
31/// - A [`Register`] implementation for schema registration at runtime.
32/// - Static query methods such as `all()`, `filter(expr)`,
33///   `filter_by_<field>()`, and `get_by_<key>()`.
34/// - Instance methods `update()` and `delete()`.
35/// - A `Fields` struct returned by `<Model>::fields()` for building typed
36///   filter expressions.
37///
38/// The struct must have named fields and no generic parameters.
39///
40/// [`Model`]: toasty::schema::Model
41/// [`Load`]: toasty::schema::Load
42/// [`Register`]: toasty::schema::Register
43///
44/// # Struct-level attributes
45///
46/// ## `#[key(...)]` — primary key
47///
48/// Defines the primary key at the struct level. Mutually exclusive with
49/// field-level `#[key]`.
50///
51/// **Simple form** — every listed field becomes a partition key:
52///
53/// ```
54/// # use toasty::Model;
55/// #[derive(Model)]
56/// #[key(name)]
57/// struct Widget {
58///     name: String,
59///     value: i64,
60/// }
61/// ```
62///
63/// **Composite key with partition/local scoping:**
64///
65/// ```
66/// # use toasty::Model;
67/// #[derive(Model)]
68/// #[key(partition = user_id, local = id)]
69/// struct Todo {
70///     #[auto]
71///     id: uuid::Uuid,
72///     user_id: String,
73///     title: String,
74/// }
75/// ```
76///
77/// The `partition` fields determine data distribution (relevant for
78/// DynamoDB); `local` fields scope within a partition. For SQL databases
79/// both behave as a regular composite primary key.
80///
81/// Multiple `partition` and `local` entries are allowed:
82///
83/// ```
84/// # use toasty::Model;
85/// # #[derive(Model)]
86/// #[key(partition = tenant, partition = org, local = id)]
87/// # struct Example { tenant: String, org: String, id: String }
88/// ```
89///
90/// When using named `partition`/`local` syntax, at least one of each is
91/// required. You cannot mix the simple and named forms.
92///
93/// ## `#[table = "name"]` — custom table name
94///
95/// Overrides the default table name. Without this attribute the table name
96/// is the pluralized, snake_case form of the struct name (e.g. `User` →
97/// `users`).
98///
99/// ```
100/// # use toasty::Model;
101/// #[derive(Model)]
102/// #[table = "legacy_users"]
103/// struct User {
104///     #[key]
105///     #[auto]
106///     id: i64,
107///     name: String,
108/// }
109/// ```
110///
111/// # Field-level attributes
112///
113/// ## `#[key]` — mark a field as a primary key column
114///
115/// Marks one or more fields as the primary key. When used on multiple
116/// fields each becomes a partition key column (equivalent to listing them
117/// in `#[key(...)]` at the struct level).
118///
119/// Cannot be combined with a struct-level `#[key(...)]` attribute.
120///
121/// ```
122/// # use toasty::Model;
123/// #[derive(Model)]
124/// struct User {
125///     #[key]
126///     #[auto]
127///     id: i64,
128///     name: String,
129/// }
130/// ```
131///
132/// ## `#[auto]` — automatic value generation
133///
134/// Tells Toasty to generate this field's value automatically. The strategy
135/// depends on the field type and optional arguments:
136///
137/// | Syntax | Behavior |
138/// |--------|----------|
139/// | `#[auto]` on `uuid::Uuid` | UUID v7 (timestamp-sortable) |
140/// | `#[auto(uuid(v4))]` | UUID v4 (random) |
141/// | `#[auto(uuid(v7))]` | UUID v7 (explicit) |
142/// | `#[auto]` on integer types (`i8`–`i64`, `u8`–`u64`) | Auto-increment |
143/// | `#[auto(increment)]` | Auto-increment (explicit) |
144/// | `#[auto]` on a field named `created_at` | Expands to `#[default(jiff::Timestamp::now())]` |
145/// | `#[auto]` on a field named `updated_at` | Expands to `#[update(jiff::Timestamp::now())]` |
146///
147/// The `created_at`/`updated_at` expansion requires the `jiff` feature and
148/// a field type compatible with `jiff::Timestamp`.
149///
150/// Cannot be combined with `#[default]` or `#[update]` on the same field.
151///
152/// ## `#[default(expr)]` — default value on create
153///
154/// Sets a default value that is used when the field is not explicitly
155/// provided during creation. The expression is any valid Rust expression.
156///
157/// ```
158/// # use toasty::Model;
159/// # #[derive(Model)]
160/// # struct Example {
161/// #     #[key]
162/// #     #[auto]
163/// #     id: i64,
164/// #[default(0)]
165/// view_count: i64,
166///
167/// #[default("draft".to_string())]
168/// status: String,
169/// # }
170/// ```
171///
172/// The default can be overridden by calling the corresponding setter on the
173/// create builder.
174///
175/// Cannot be combined with `#[auto]` on the same field. Can be combined
176/// with `#[update]` (the default applies on create; the update expression
177/// applies on subsequent updates).
178///
179/// ## `#[update(expr)]` — value applied on create and update
180///
181/// Sets a value that Toasty applies every time a record is created or
182/// updated, unless the field is explicitly set on the builder.
183///
184/// ```
185/// # use toasty::Model;
186/// # #[derive(Model)]
187/// # struct Example {
188/// #     #[key]
189/// #     #[auto]
190/// #     id: i64,
191/// #[update(jiff::Timestamp::now())]
192/// updated_at: jiff::Timestamp,
193/// # }
194/// ```
195///
196/// Cannot be combined with `#[auto]` on the same field.
197///
198/// ## `#[index]` — add a database index
199///
200/// Creates a non-unique index on the field. Toasty generates a
201/// `filter_by_<field>` method for indexed fields.
202///
203/// ```
204/// # use toasty::Model;
205/// # #[derive(Model)]
206/// # struct Example {
207/// #     #[key]
208/// #     #[auto]
209/// #     id: i64,
210/// #[index]
211/// email: String,
212/// # }
213/// ```
214///
215/// ## `#[unique]` — add a unique constraint
216///
217/// Creates a unique index on the field. Like `#[index]`, this generates
218/// `filter_by_<field>`. The database enforces uniqueness.
219///
220/// ```
221/// # use toasty::Model;
222/// # #[derive(Model)]
223/// # struct Example {
224/// #     #[key]
225/// #     #[auto]
226/// #     id: i64,
227/// #[unique]
228/// email: String,
229/// # }
230/// ```
231///
232/// ## `#[column(...)]` — customize the database column
233///
234/// Overrides the column name and/or type for a field.
235///
236/// **Custom name:**
237///
238/// ```
239/// # use toasty::Model;
240/// # #[derive(Model)]
241/// # struct Example {
242/// #     #[key]
243/// #     #[auto]
244/// #     id: i64,
245/// #[column("user_email")]
246/// email: String,
247/// # }
248/// ```
249///
250/// **Custom type:**
251///
252/// ```
253/// # use toasty::Model;
254/// # #[derive(Model)]
255/// # struct Example {
256/// #     #[key]
257/// #     #[auto]
258/// #     id: i64,
259/// #[column(type = varchar(255))]
260/// email: String,
261/// # }
262/// ```
263///
264/// **Both:**
265///
266/// ```
267/// # use toasty::Model;
268/// # #[derive(Model)]
269/// # struct Example {
270/// #     #[key]
271/// #     #[auto]
272/// #     id: i64,
273/// #[column("user_email", type = varchar(255))]
274/// email: String,
275/// # }
276/// ```
277///
278/// ### Supported column types
279///
280/// | Syntax | Description |
281/// |--------|-------------|
282/// | `boolean` | Boolean |
283/// | `i8`, `i16`, `i32`, `i64` | Signed integer (1/2/4/8 bytes) |
284/// | `int(N)` | Signed integer with N-byte width |
285/// | `u8`, `u16`, `u32`, `u64` | Unsigned integer (1/2/4/8 bytes) |
286/// | `uint(N)` | Unsigned integer with N-byte width |
287/// | `text` | Unbounded text |
288/// | `varchar(N)` | Text with max length N |
289/// | `numeric` | Arbitrary-precision numeric |
290/// | `numeric(P, S)` | Numeric with precision P and scale S |
291/// | `binary(N)` | Fixed-size binary with N bytes |
292/// | `blob` | Variable-length binary |
293/// | `timestamp(P)` | Timestamp with P fractional-second digits |
294/// | `date` | Date without time |
295/// | `time(P)` | Time with P fractional-second digits |
296/// | `datetime(P)` | Date and time with P fractional-second digits |
297/// | `"custom"` | Arbitrary type string passed through to the driver |
298///
299/// Cannot be used on relation fields.
300///
301/// ## `#[serialize(json)]` — serialize complex types as JSON
302///
303/// Stores the field as a JSON string in the database. Requires the `serde`
304/// feature and that the field type implements `serde::Serialize` and
305/// `serde::Deserialize`.
306///
307/// ```
308/// # use toasty::Model;
309/// # #[derive(Model)]
310/// # struct Example {
311/// #     #[key]
312/// #     #[auto]
313/// #     id: i64,
314/// #[serialize(json)]
315/// tags: Vec<String>,
316/// # }
317/// ```
318///
319/// For `Option<T>` fields, add `nullable` so that `None` maps to SQL
320/// `NULL` rather than the JSON string `"null"`:
321///
322/// ```
323/// # use toasty::Model;
324/// # use std::collections::HashMap;
325/// # #[derive(Model)]
326/// # struct Example {
327/// #     #[key]
328/// #     #[auto]
329/// #     id: i64,
330/// #[serialize(json, nullable)]
331/// metadata: Option<HashMap<String, String>>,
332/// # }
333/// ```
334///
335/// Cannot be used on relation fields.
336///
337/// # Relation attributes
338///
339/// ## `#[belongs_to(...)]` — foreign-key reference
340///
341/// Declares a many-to-one (or one-to-one) association through a foreign
342/// key stored on this model.
343///
344/// ```
345/// # use toasty::Model;
346/// # #[derive(Model)]
347/// # struct User {
348/// #     #[key]
349/// #     #[auto]
350/// #     id: i64,
351/// # }
352/// # #[derive(Model)]
353/// # struct Example {
354/// #     #[key]
355/// #     #[auto]
356/// #     id: i64,
357/// #     user_id: i64,
358/// #[belongs_to(key = user_id, references = id)]
359/// user: toasty::BelongsTo<User>,
360/// # }
361/// ```
362///
363/// | Parameter | Meaning |
364/// |-----------|---------|
365/// | `key = <field>` | Local field holding the foreign key value |
366/// | `references = <field>` | Field on the target model being referenced |
367///
368/// For composite foreign keys, repeat `key`/`references` pairs:
369///
370/// ```
371/// # use toasty::Model;
372/// # #[derive(Model)]
373/// # struct Org {
374/// #     #[key]
375/// #     id: i64,
376/// #     #[key]
377/// #     tenant_id: i64,
378/// # }
379/// # #[derive(Model)]
380/// # struct Example {
381/// #     #[key]
382/// #     #[auto]
383/// #     id: i64,
384/// #     org_id: i64,
385/// #     tenant_id: i64,
386/// #[belongs_to(key = org_id, references = id, key = tenant_id, references = tenant_id)]
387/// org: toasty::BelongsTo<Org>,
388/// # }
389/// ```
390///
391/// The number of `key` entries must equal the number of `references`
392/// entries.
393///
394/// Wrap the target type in `Option` for an optional (nullable) foreign key:
395///
396/// ```
397/// # use toasty::Model;
398/// # #[derive(Model)]
399/// # struct User {
400/// #     #[key]
401/// #     #[auto]
402/// #     id: i64,
403/// # }
404/// # #[derive(Model)]
405/// # struct Example {
406/// #     #[key]
407/// #     #[auto]
408/// #     id: i64,
409/// #[index]
410/// manager_id: Option<i64>,
411///
412/// #[belongs_to(key = manager_id, references = id)]
413/// manager: toasty::BelongsTo<Option<User>>,
414/// # }
415/// ```
416///
417/// ## `#[has_many]` — one-to-many association
418///
419/// Declares a collection of related models. The target model must have a
420/// `#[belongs_to]` field pointing back to this model.
421///
422/// ```
423/// # use toasty::Model;
424/// # #[derive(Model)]
425/// # struct Post {
426/// #     #[key]
427/// #     #[auto]
428/// #     id: i64,
429/// #     #[index]
430/// #     example_id: i64,
431/// #     #[belongs_to(key = example_id, references = id)]
432/// #     example: toasty::BelongsTo<Example>,
433/// # }
434/// # #[derive(Model)]
435/// # struct Example {
436/// #     #[key]
437/// #     #[auto]
438/// #     id: i64,
439/// #[has_many]
440/// posts: toasty::HasMany<Post>,
441/// # }
442/// ```
443///
444/// Toasty generates an accessor method (e.g. `.posts()`) and an insert
445/// helper (e.g. `.insert_post()`), where the insert helper name is the
446/// auto-singularized field name.
447///
448/// ### `pair` — disambiguate self-referential or multiple relations
449///
450/// When the target model has more than one `#[belongs_to]` pointing to
451/// the same model (or points to itself), use `pair` to specify which
452/// `belongs_to` field this `has_many` corresponds to:
453///
454/// ```
455/// # use toasty::Model;
456/// # #[derive(Model)]
457/// # struct Person {
458/// #     #[key]
459/// #     #[auto]
460/// #     id: i64,
461/// #     #[index]
462/// #     parent_id: Option<i64>,
463/// #     #[belongs_to(key = parent_id, references = id)]
464/// #     parent: toasty::BelongsTo<Option<Self>>,
465/// #[has_many(pair = parent)]
466/// children: toasty::HasMany<Person>,
467/// # }
468/// ```
469///
470/// ## `#[has_one]` — one-to-one association
471///
472/// Declares a single related model. The target model must have a
473/// `#[belongs_to]` field pointing back to this model.
474///
475/// ```
476/// # use toasty::Model;
477/// # #[derive(Model)]
478/// # struct Profile {
479/// #     #[key]
480/// #     #[auto]
481/// #     id: i64,
482/// #     #[index]
483/// #     example_id: i64,
484/// #     #[belongs_to(key = example_id, references = id)]
485/// #     example: toasty::BelongsTo<Example>,
486/// # }
487/// # #[derive(Model)]
488/// # struct Example {
489/// #     #[key]
490/// #     #[auto]
491/// #     id: i64,
492/// #[has_one]
493/// profile: toasty::HasOne<Profile>,
494/// # }
495/// ```
496///
497/// Wrap in `Option` for an optional association:
498///
499/// ```
500/// # use toasty::Model;
501/// # #[derive(Model)]
502/// # struct Profile {
503/// #     #[key]
504/// #     #[auto]
505/// #     id: i64,
506/// #     #[index]
507/// #     example_id: i64,
508/// #     #[belongs_to(key = example_id, references = id)]
509/// #     example: toasty::BelongsTo<Example>,
510/// # }
511/// # #[derive(Model)]
512/// # struct Example {
513/// #     #[key]
514/// #     #[auto]
515/// #     id: i64,
516/// #[has_one]
517/// profile: toasty::HasOne<Option<Profile>>,
518/// # }
519/// ```
520///
521/// # Constraints
522///
523/// - The struct must have named fields (tuple structs are not supported).
524/// - Generic parameters are not supported.
525/// - Every root model must have a primary key, defined either by a
526///   struct-level `#[key(...)]` or by one or more field-level `#[key]`
527///   attributes, but not both.
528/// - `#[auto]` cannot be combined with `#[default]` or `#[update]` on the
529///   same field.
530/// - `#[column]`, `#[default]`, `#[update]`, and `#[serialize]` cannot be
531///   used on relation fields (`BelongsTo`, `HasMany`, `HasOne`).
532/// - A field can have at most one relation attribute.
533/// - `Self` can be used as a type in relation fields for self-referential
534///   models.
535///
536/// # Full example
537///
538/// ```
539/// #[derive(Debug, toasty::Model)]
540/// struct User {
541///     #[key]
542///     #[auto]
543///     id: i64,
544///
545///     #[unique]
546///     email: String,
547///
548///     name: String,
549///
550///     #[default(jiff::Timestamp::now())]
551///     created_at: jiff::Timestamp,
552///
553///     #[update(jiff::Timestamp::now())]
554///     updated_at: jiff::Timestamp,
555///
556///     #[has_many]
557///     posts: toasty::HasMany<Post>,
558/// }
559///
560/// #[derive(Debug, toasty::Model)]
561/// struct Post {
562///     #[key]
563///     #[auto]
564///     id: i64,
565///
566///     title: String,
567///
568///     #[serialize(json)]
569///     tags: Vec<String>,
570///
571///     #[index]
572///     user_id: i64,
573///
574///     #[belongs_to(key = user_id, references = id)]
575///     user: toasty::BelongsTo<User>,
576/// }
577/// ```
578#[proc_macro_derive(
579    Model,
580    attributes(
581        key, auto, default, update, column, index, unique, table, has_many, has_one, belongs_to,
582        serialize
583    )
584)]
585pub fn derive_model(input: TokenStream) -> TokenStream {
586    match model::generate_model(input.into()) {
587        Ok(output) => output.into(),
588        Err(e) => e.to_compile_error().into(),
589    }
590}
591
592/// Derive macro that turns a struct or enum into an embedded type stored
593/// inline in a parent model's table.
594///
595/// Embedded types do not have their own tables or primary keys. Their
596/// fields are flattened into the parent model's columns. Use `Embed` for
597/// value objects (addresses, coordinates, metadata) and enums
598/// (status codes, contact info variants).
599///
600/// # Structs
601///
602/// An embedded struct's fields become columns in the parent table, prefixed
603/// with the field name. For example, an `address: Address` field with
604/// `street` and `city` produces columns `address_street` and
605/// `address_city`.
606///
607/// ```
608/// #[derive(toasty::Embed)]
609/// struct Address {
610///     street: String,
611///     city: String,
612/// }
613///
614/// #[derive(toasty::Model)]
615/// struct User {
616///     #[key]
617///     #[auto]
618///     id: i64,
619///     name: String,
620///     address: Address,
621/// }
622/// ```
623///
624/// Applying `#[derive(Embed)]` to a struct generates:
625///
626/// - An [`Embed`] trait implementation (which extends [`Register`]).
627/// - A `Fields` struct returned by `<Type>::fields()` for building
628///   filter expressions on individual fields.
629/// - An `Update` struct used by the parent model's update builder for
630///   partial field updates.
631///
632/// ## Nesting
633///
634/// Embedded structs can contain other embedded types. Columns are
635/// flattened with chained prefixes:
636///
637/// ```
638/// #[derive(toasty::Embed)]
639/// struct Location {
640///     lat: i64,
641///     lon: i64,
642/// }
643///
644/// #[derive(toasty::Embed)]
645/// struct Address {
646///     street: String,
647///     city: Location,
648/// }
649/// ```
650///
651/// When `Address` is embedded as `address` in a parent model, this
652/// produces columns `address_street`, `address_city_lat`, and
653/// `address_city_lon`.
654///
655/// # Enums
656///
657/// An embedded enum stores a discriminant value identifying the active
658/// variant. Each variant must have a `#[column(variant = N)]` attribute
659/// assigning a stable integer discriminant.
660///
661/// **Unit-only enum:**
662///
663/// ```
664/// #[derive(toasty::Embed)]
665/// enum Status {
666///     #[column(variant = 1)]
667///     Pending,
668///     #[column(variant = 2)]
669///     Active,
670///     #[column(variant = 3)]
671///     Archived,
672/// }
673/// ```
674///
675/// A unit-only enum occupies a single column in the parent table. The
676/// column stores the discriminant as an integer.
677///
678/// **Data-carrying enum:**
679///
680/// ```
681/// #[derive(toasty::Embed)]
682/// enum ContactInfo {
683///     #[column(variant = 1)]
684///     Email { address: String },
685///     #[column(variant = 2)]
686///     Phone { number: String },
687/// }
688/// ```
689///
690/// A data-carrying enum stores the discriminant column plus one nullable
691/// column per variant field. For example, a `contact: ContactInfo` field
692/// produces columns `contact` (discriminant), `contact_address`, and
693/// `contact_number`. Only the columns belonging to the active variant
694/// contain values; the rest are `NULL`.
695///
696/// **Mixed enum** (unit and data variants together):
697///
698/// ```
699/// #[derive(toasty::Embed)]
700/// enum Status {
701///     #[column(variant = 1)]
702///     Pending,
703///     #[column(variant = 2)]
704///     Failed { reason: String },
705///     #[column(variant = 3)]
706///     Done,
707/// }
708/// ```
709///
710/// Applying `#[derive(Embed)]` to an enum generates:
711///
712/// - An [`Embed`] trait implementation (which extends [`Register`]).
713/// - A `Fields` struct with `is_<variant>()` methods and comparison
714///   methods (`eq`, `ne`, `in_list`).
715/// - For data-carrying variants, per-variant handle types with a
716///   `matches(closure)` method for pattern matching and field access.
717///
718/// # Field-level attributes
719///
720/// ## `#[column(...)]` — customize the database column
721///
722/// **On struct fields**, overrides the column name and/or type:
723///
724/// ```
725/// #[derive(toasty::Embed)]
726/// struct Address {
727///     #[column("addr_street")]
728///     street: String,
729///
730///     #[column(type = varchar(255))]
731///     city: String,
732/// }
733/// ```
734///
735/// See [`Model`][`derive@Model`] for the full list of supported column
736/// types.
737///
738/// **On enum variants**, `#[column(variant = N)]` is **required** and
739/// assigns the integer discriminant stored in the database:
740///
741/// ```
742/// # #[derive(toasty::Embed)]
743/// # enum Example {
744/// #[column(variant = 1)]
745/// Pending,
746/// # }
747/// ```
748///
749/// Discriminant values must be unique across all variants of the enum.
750/// They are stored as `i64`.
751///
752/// ## `#[index]` — add a database index
753///
754/// Creates a non-unique index on the field's flattened column.
755///
756/// ```
757/// #[derive(toasty::Embed)]
758/// struct Contact {
759///     #[index]
760///     country: String,
761/// }
762/// ```
763///
764/// ## `#[unique]` — add a unique constraint
765///
766/// Creates a unique index on the field's flattened column. The database
767/// enforces uniqueness.
768///
769/// ```
770/// #[derive(toasty::Embed)]
771/// struct Contact {
772///     #[unique]
773///     email: String,
774/// }
775/// ```
776///
777/// # Using embedded types in a model
778///
779/// Reference an embedded type as a field on a [`Model`][`derive@Model`]
780/// struct. The parent model's create and update builders gain a setter for
781/// the embedded field. For embedded structs, a `with_<field>` method
782/// supports partial updates of individual sub-fields:
783///
784/// ```no_run
785/// # #[derive(toasty::Embed)]
786/// # struct Address { street: String, city: String }
787/// # #[derive(toasty::Model)]
788/// # struct User {
789/// #     #[key]
790/// #     #[auto]
791/// #     id: i64,
792/// #     name: String,
793/// #     address: Address,
794/// # }
795/// # async fn example(mut db: toasty::Db, mut user: User) -> toasty::Result<()> {
796/// // Full replacement
797/// user.update()
798///     .address(Address { street: "456 Oak Ave".into(), city: "Seattle".into() })
799///     .exec(&mut db).await?;
800///
801/// // Partial update (struct only) — updates city, leaves street unchanged
802/// user.update()
803///     .with_address(|a| { a.city("Portland"); })
804///     .exec(&mut db).await?;
805/// # Ok(())
806/// # }
807/// ```
808///
809/// Embedded struct fields are queryable through the parent model's
810/// `fields()` accessor:
811///
812/// ```no_run
813/// # #[derive(toasty::Embed)]
814/// # struct Address { street: String, city: String }
815/// # #[derive(toasty::Model)]
816/// # struct User {
817/// #     #[key]
818/// #     #[auto]
819/// #     id: i64,
820/// #     name: String,
821/// #     address: Address,
822/// # }
823/// # async fn example(mut db: toasty::Db) -> toasty::Result<()> {
824/// let users = User::filter(User::fields().address().city().eq("Seattle"))
825///     .exec(&mut db).await?;
826/// # Ok(())
827/// # }
828/// ```
829///
830/// # Constraints
831///
832/// - Embedded structs must have named fields (tuple structs are not
833///   supported).
834/// - Generic parameters are not supported.
835/// - Every enum variant must have a `#[column(variant = N)]` attribute
836///   with a unique discriminant value.
837/// - Enum variants may be unit variants or have named fields. Tuple
838///   variants are not supported.
839/// - Embedded types cannot have primary keys, relations, `#[auto]`,
840///   `#[default]`, `#[update]`, or `#[serialize]` attributes.
841///
842/// # Full example
843///
844/// ```no_run
845/// # async fn example(mut db: toasty::Db) -> toasty::Result<()> {
846/// #[derive(Debug, PartialEq, toasty::Embed)]
847/// enum Priority {
848///     #[column(variant = 1)]
849///     Low,
850///     #[column(variant = 2)]
851///     Normal,
852///     #[column(variant = 3)]
853///     High,
854/// }
855///
856/// #[derive(Debug, toasty::Embed)]
857/// struct Metadata {
858///     version: i64,
859///     status: String,
860///     priority: Priority,
861/// }
862///
863/// #[derive(Debug, toasty::Model)]
864/// struct Document {
865///     #[key]
866///     #[auto]
867///     id: i64,
868///
869///     title: String,
870///
871///     #[unique]
872///     slug: String,
873///
874///     meta: Metadata,
875/// }
876///
877/// // Create
878/// let mut doc = Document::create()
879///     .title("Design doc")
880///     .slug("design-doc")
881///     .meta(Metadata {
882///         version: 1,
883///         status: "draft".to_string(),
884///         priority: Priority::Normal,
885///     })
886///     .exec(&mut db).await?;
887///
888/// // Query by embedded field
889/// let drafts = Document::filter(
890///     Document::fields().meta().status().eq("draft")
891/// ).exec(&mut db).await?;
892///
893/// // Partial update
894/// doc.update()
895///     .with_meta(|m| { m.version(2).status("published"); })
896///     .exec(&mut db).await?;
897/// # Ok(())
898/// # }
899/// ```
900///
901/// [`Embed`]: toasty::Embed
902/// [`Register`]: toasty::Register
903#[proc_macro_derive(Embed, attributes(column, index, unique))]
904pub fn derive_embed(input: TokenStream) -> TokenStream {
905    match model::generate_embed(input.into()) {
906        Ok(output) => output.into(),
907        Err(e) => e.to_compile_error().into(),
908    }
909}
910
911/// Builds a query using the Toasty query language. The macro expands into
912/// the equivalent method-chain calls on the query builder API. It does
913/// not execute the query — chain `.exec(&mut db).await?` on the result to run
914/// it.
915///
916/// # Syntax
917///
918/// ```text
919/// query!(Source [FILTER expr] [ORDER BY .field ASC|DESC] [OFFSET n] [LIMIT n])
920/// ```
921///
922/// `Source` is a model type path (e.g., `User`). All clauses are optional and
923/// can appear in any combination, but must follow the order shown above when
924/// present. All keywords are case-insensitive: `FILTER`, `filter`, and `Filter`
925/// all work.
926///
927/// # Basic queries
928///
929/// With no clauses, `query!` returns all records of the given model.
930///
931/// ```
932/// # #[derive(toasty::Model)]
933/// # struct User {
934/// #     #[key]
935/// #     id: i64,
936/// #     name: String,
937/// #     age: i64,
938/// #     active: bool,
939/// # }
940/// // Returns all users — expands to User::all()
941/// let _ = toasty::query!(User);
942/// ```
943///
944/// # Filter expressions
945///
946/// The `FILTER` clause accepts an expression built from field comparisons,
947/// boolean operators, and external references.
948///
949/// ## Comparison operators
950///
951/// Dot-prefixed field paths (`.name`, `.age`) refer to fields on the source
952/// model. The right-hand side is a literal or external reference.
953///
954/// | Operator | Expansion         |
955/// |----------|-------------------|
956/// | `==`     | `.eq(val)`        |
957/// | `!=`     | `.ne(val)`        |
958/// | `>`      | `.gt(val)`        |
959/// | `>=`     | `.ge(val)`        |
960/// | `<`      | `.lt(val)`        |
961/// | `<=`     | `.le(val)`        |
962///
963/// ```
964/// # #[derive(toasty::Model)]
965/// # struct User {
966/// #     #[key]
967/// #     id: i64,
968/// #     name: String,
969/// #     age: i64,
970/// #     active: bool,
971/// # }
972/// // Equality — expands to User::filter(User::fields().name().eq("Alice"))
973/// let _ = toasty::query!(User FILTER .name == "Alice");
974///
975/// // Not equal
976/// let _ = toasty::query!(User FILTER .name != "Bob");
977///
978/// // Greater than
979/// let _ = toasty::query!(User FILTER .age > 18);
980///
981/// // Greater than or equal
982/// let _ = toasty::query!(User FILTER .age >= 21);
983///
984/// // Less than
985/// let _ = toasty::query!(User FILTER .age < 65);
986///
987/// // Less than or equal
988/// let _ = toasty::query!(User FILTER .age <= 99);
989/// ```
990///
991/// ## Boolean operators
992///
993/// `AND`, `OR`, and `NOT` combine filter expressions. Precedence follows
994/// standard boolean logic: `NOT` binds tightest, then `AND`, then `OR`.
995///
996/// ```
997/// # #[derive(toasty::Model)]
998/// # struct User {
999/// #     #[key]
1000/// #     id: i64,
1001/// #     name: String,
1002/// #     age: i64,
1003/// #     active: bool,
1004/// # }
1005/// // AND — both conditions must match
1006/// let _ = toasty::query!(User FILTER .name == "Alice" AND .age > 18);
1007///
1008/// // OR — either condition matches
1009/// let _ = toasty::query!(User FILTER .name == "Alice" OR .name == "Bob");
1010///
1011/// // NOT — negates the following expression
1012/// let _ = toasty::query!(User FILTER NOT .active == true);
1013///
1014/// // Combining all three
1015/// let _ = toasty::query!(User FILTER NOT .active == true AND (.name == "Alice" OR .age >= 21));
1016/// ```
1017///
1018/// ## Operator precedence
1019///
1020/// Without parentheses, `NOT` binds tightest, then `AND`, then `OR`. Use
1021/// parentheses to override.
1022///
1023/// ```
1024/// # #[derive(toasty::Model)]
1025/// # struct User {
1026/// #     #[key]
1027/// #     id: i64,
1028/// #     name: String,
1029/// #     age: i64,
1030/// #     active: bool,
1031/// # }
1032/// // Without parens: parsed as (.name == "A" AND .age > 0) OR .active == false
1033/// let _ = toasty::query!(User FILTER .name == "A" AND .age > 0 OR .active == false);
1034///
1035/// // With parens: forces OR to bind first
1036/// let _ = toasty::query!(User FILTER .name == "A" AND (.age > 0 OR .active == false));
1037/// ```
1038///
1039/// ## Boolean and integer literals
1040///
1041/// Boolean fields can be compared against `true` and `false` literals.
1042/// Integer literals work as expected.
1043///
1044/// ```
1045/// # #[derive(toasty::Model)]
1046/// # struct User {
1047/// #     #[key]
1048/// #     id: i64,
1049/// #     name: String,
1050/// #     age: i64,
1051/// #     active: bool,
1052/// # }
1053/// let _ = toasty::query!(User FILTER .active == true);
1054/// let _ = toasty::query!(User FILTER .active == false);
1055/// let _ = toasty::query!(User FILTER .age == 42);
1056/// ```
1057///
1058/// # Referencing surrounding code
1059///
1060/// `#ident` pulls a variable from the surrounding scope. `#(expr)` embeds an
1061/// arbitrary Rust expression.
1062///
1063/// ```
1064/// # #[derive(toasty::Model)]
1065/// # struct User {
1066/// #     #[key]
1067/// #     id: i64,
1068/// #     name: String,
1069/// #     age: i64,
1070/// #     active: bool,
1071/// # }
1072/// // Variable reference — expands to User::filter(User::fields().name().eq(name))
1073/// let name = "Carl";
1074/// let _ = toasty::query!(User FILTER .name == #name);
1075///
1076/// // Expression reference
1077/// fn min_age() -> i64 { 18 }
1078/// let _ = toasty::query!(User FILTER .age > #(min_age()));
1079/// ```
1080///
1081/// # Dot-prefixed field paths
1082///
1083/// A leading `.` starts a field path rooted at the source model's `fields()`
1084/// method. Chained dots navigate multi-segment paths.
1085///
1086/// ```
1087/// # #[derive(toasty::Model)]
1088/// # struct User {
1089/// #     #[key]
1090/// #     id: i64,
1091/// #     name: String,
1092/// #     age: i64,
1093/// #     active: bool,
1094/// # }
1095/// // .name expands to User::fields().name()
1096/// let _ = toasty::query!(User FILTER .name == "Alice");
1097///
1098/// // Multiple fields in a single expression
1099/// let _ = toasty::query!(User FILTER .id == 1 AND .name == "X" AND .age > 0);
1100/// ```
1101///
1102/// # ORDER BY
1103///
1104/// Sort results by a field in ascending (`ASC`) or descending (`DESC`) order.
1105/// If no direction is specified, ascending is the default.
1106///
1107/// ```
1108/// # #[derive(toasty::Model)]
1109/// # struct User {
1110/// #     #[key]
1111/// #     id: i64,
1112/// #     name: String,
1113/// #     age: i64,
1114/// #     active: bool,
1115/// # }
1116/// // Ascending order (explicit)
1117/// let _ = toasty::query!(User ORDER BY .name ASC);
1118///
1119/// // Descending order
1120/// let _ = toasty::query!(User ORDER BY .age DESC);
1121///
1122/// // Combined with filter
1123/// let _ = toasty::query!(User FILTER .active == true ORDER BY .name ASC);
1124/// ```
1125///
1126/// # LIMIT and OFFSET
1127///
1128/// `LIMIT` restricts the number of returned records. `OFFSET` skips a number
1129/// of records before returning. Both accept integer literals, `#ident`
1130/// variables, and `#(expr)` expressions.
1131///
1132/// ```
1133/// # #[derive(toasty::Model)]
1134/// # struct User {
1135/// #     #[key]
1136/// #     id: i64,
1137/// #     name: String,
1138/// #     age: i64,
1139/// #     active: bool,
1140/// # }
1141/// // Return at most 10 records
1142/// let _ = toasty::query!(User LIMIT 10);
1143///
1144/// // Skip 20, then return 10
1145/// let _ = toasty::query!(User OFFSET 20 LIMIT 10);
1146///
1147/// // Variable pagination
1148/// let page_size = 25usize;
1149/// let _ = toasty::query!(User LIMIT #page_size);
1150///
1151/// // Expression pagination
1152/// let _ = toasty::query!(User LIMIT #(5 + 5));
1153/// ```
1154///
1155/// # Combining clauses
1156///
1157/// All clauses can be combined. When present, they must appear in this order:
1158/// `FILTER`, `ORDER BY`, `OFFSET`, `LIMIT`.
1159///
1160/// ```
1161/// # #[derive(toasty::Model)]
1162/// # struct User {
1163/// #     #[key]
1164/// #     id: i64,
1165/// #     name: String,
1166/// #     age: i64,
1167/// #     active: bool,
1168/// # }
1169/// let _ = toasty::query!(User FILTER .active == true ORDER BY .name ASC LIMIT 10);
1170/// let _ = toasty::query!(User FILTER .age > 18 ORDER BY .age DESC OFFSET 0 LIMIT 50);
1171/// ```
1172///
1173/// # Case-insensitive keywords
1174///
1175/// All keywords — `FILTER`, `AND`, `OR`, `NOT`, `ORDER`, `BY`, `ASC`, `DESC`,
1176/// `OFFSET`, `LIMIT` — are matched case-insensitively. Any casing works.
1177///
1178/// ```
1179/// # #[derive(toasty::Model)]
1180/// # struct User {
1181/// #     #[key]
1182/// #     id: i64,
1183/// #     name: String,
1184/// #     age: i64,
1185/// #     active: bool,
1186/// # }
1187/// // These are all equivalent
1188/// let _ = toasty::query!(User FILTER .name == "A");
1189/// let _ = toasty::query!(User filter .name == "A");
1190/// let _ = toasty::query!(User Filter .name == "A");
1191/// ```
1192///
1193/// # Expansion details
1194///
1195/// The macro translates each syntactic element into method-chain calls on the
1196/// query builder.
1197///
1198/// ## No filter
1199///
1200/// ```text
1201/// query!(User)          →  User::all()
1202/// ```
1203///
1204/// ## Filter
1205///
1206/// ```text
1207/// query!(User FILTER .name == "A")
1208///     →  User::filter(User::fields().name().eq("A"))
1209/// ```
1210///
1211/// ## Logical operators
1212///
1213/// ```text
1214/// query!(User FILTER .a == 1 AND .b == 2)
1215///     →  User::filter(User::fields().a().eq(1).and(User::fields().b().eq(2)))
1216///
1217/// query!(User FILTER .a == 1 OR .b == 2)
1218///     →  User::filter(User::fields().a().eq(1).or(User::fields().b().eq(2)))
1219///
1220/// query!(User FILTER NOT .a == 1)
1221///     →  User::filter((User::fields().a().eq(1)).not())
1222/// ```
1223///
1224/// ## ORDER BY
1225///
1226/// ```text
1227/// query!(User ORDER BY .name ASC)
1228///     →  { let mut q = User::all(); q = q.order_by(User::fields().name().asc()); q }
1229/// ```
1230///
1231/// ## LIMIT / OFFSET
1232///
1233/// ```text
1234/// query!(User LIMIT 10)
1235///     →  { let mut q = User::all(); q = q.limit(10); q }
1236///
1237/// query!(User OFFSET 5 LIMIT 10)
1238///     →  { let mut q = User::all(); q = q.limit(10); q = q.offset(5); q }
1239/// ```
1240///
1241/// Note: in the expansion, `limit` is called before `offset` because the
1242/// API requires it.
1243///
1244/// ## External references
1245///
1246/// ```text
1247/// let x = "Carl";
1248/// query!(User FILTER .name == #x)
1249///     →  User::filter(User::fields().name().eq(x))
1250///
1251/// query!(User FILTER .age > #(compute()))
1252///     →  User::filter(User::fields().age().gt(compute()))
1253/// ```
1254///
1255/// # Errors
1256///
1257/// The macro produces compile-time errors for:
1258///
1259/// - **Missing model path**: the first token must be a valid type path.
1260/// - **Unknown fields**: dot-prefixed paths that don't match a field on the
1261///   model produce a type error from the generated `fields()` method.
1262/// - **Type mismatches**: comparing a field to a value of the wrong type
1263///   produces a standard Rust type error (e.g., `.age == "not a number"`).
1264/// - **Unexpected tokens**: tokens after the last recognized clause cause
1265///   `"unexpected tokens after query"`.
1266/// - **Invalid clause order**: placing `FILTER` after `ORDER BY` or `LIMIT`
1267///   before `OFFSET` causes a parse error since the clauses are parsed in
1268///   fixed order.
1269/// - **Missing `BY` after `ORDER`**: writing `ORDER .name` instead of
1270///   `ORDER BY .name` produces `"expected 'BY' after 'ORDER'"`.
1271/// - **Invalid pagination value**: `LIMIT` and `OFFSET` require an integer
1272///   literal, `#variable`, or `#(expression)`.
1273#[proc_macro]
1274pub fn query(input: TokenStream) -> TokenStream {
1275    match query::generate(input.into()) {
1276        Ok(output) => output.into(),
1277        Err(e) => e.to_compile_error().into(),
1278    }
1279}
1280
1281/// Expands struct-literal syntax into create builder method chains. Returns one
1282/// or more create builders — call `.exec(&mut db).await?` to insert the
1283/// record(s).
1284///
1285/// # Syntax forms
1286///
1287/// ## Single creation
1288///
1289/// ```ignore
1290/// toasty::create!(Type { field: value, ... })
1291/// ```
1292///
1293/// Expands to `Type::create().field(value)...` and returns the model's create
1294/// builder (e.g., `UserCreate`).
1295///
1296/// ```no_run
1297/// # #[derive(toasty::Model)]
1298/// # struct User {
1299/// #     #[key]
1300/// #     #[auto]
1301/// #     id: i64,
1302/// #     name: String,
1303/// #     email: String,
1304/// # }
1305/// # async fn example(mut db: toasty::Db) -> toasty::Result<()> {
1306/// let user = toasty::create!(User {
1307///     name: "Alice",
1308///     email: "alice@example.com"
1309/// })
1310/// .exec(&mut db)
1311/// .await?;
1312/// # Ok(())
1313/// # }
1314/// ```
1315///
1316/// ## Scoped creation
1317///
1318/// ```ignore
1319/// toasty::create!(in expr { field: value, ... })
1320/// ```
1321///
1322/// Expands to `expr.create().field(value)...`. Creates a record through a
1323/// relation accessor. The foreign key is set automatically.
1324///
1325/// ```no_run
1326/// # #[derive(toasty::Model)]
1327/// # struct User {
1328/// #     #[key]
1329/// #     #[auto]
1330/// #     id: i64,
1331/// #     name: String,
1332/// #     #[has_many]
1333/// #     todos: toasty::HasMany<Todo>,
1334/// # }
1335/// # #[derive(toasty::Model)]
1336/// # struct Todo {
1337/// #     #[key]
1338/// #     #[auto]
1339/// #     id: i64,
1340/// #     title: String,
1341/// #     #[index]
1342/// #     user_id: i64,
1343/// #     #[belongs_to(key = user_id, references = id)]
1344/// #     user: toasty::BelongsTo<User>,
1345/// # }
1346/// # async fn example(mut db: toasty::Db, user: User) -> toasty::Result<()> {
1347/// let todo = toasty::create!(in user.todos() { title: "buy milk" })
1348///     .exec(&mut db)
1349///     .await?;
1350///
1351/// // todo.user_id == user.id
1352/// # Ok(())
1353/// # }
1354/// ```
1355///
1356/// ## Typed batch
1357///
1358/// ```ignore
1359/// toasty::create!(Type::[ { fields }, { fields }, ... ])
1360/// ```
1361///
1362/// Expands to `toasty::batch([builder1, builder2, ...])` and returns
1363/// `Vec<Type>` when executed:
1364///
1365/// ```no_run
1366/// # #[derive(toasty::Model)]
1367/// # struct User {
1368/// #     #[key]
1369/// #     #[auto]
1370/// #     id: i64,
1371/// #     name: String,
1372/// # }
1373/// # async fn example(mut db: toasty::Db) -> toasty::Result<()> {
1374/// let users = toasty::create!(User::[
1375///     { name: "Alice" },
1376///     { name: "Bob" },
1377/// ])
1378/// .exec(&mut db)
1379/// .await?;
1380/// // users: Vec<User>
1381/// # Ok(())
1382/// # }
1383/// ```
1384///
1385/// ## Tuple
1386///
1387/// ```ignore
1388/// toasty::create!((
1389///     Type1 { fields },
1390///     Type2 { fields },
1391///     ...
1392/// ))
1393/// ```
1394///
1395/// Expands to `toasty::batch((builder1, builder2, ...))` and returns a
1396/// tuple matching the input types:
1397///
1398/// ```no_run
1399/// # #[derive(toasty::Model)]
1400/// # struct User {
1401/// #     #[key]
1402/// #     #[auto]
1403/// #     id: i64,
1404/// #     name: String,
1405/// # }
1406/// # #[derive(toasty::Model)]
1407/// # struct Post {
1408/// #     #[key]
1409/// #     #[auto]
1410/// #     id: i64,
1411/// #     title: String,
1412/// # }
1413/// # async fn example(mut db: toasty::Db) -> toasty::Result<()> {
1414/// let (user, post) = toasty::create!((
1415///     User { name: "Alice" },
1416///     Post { title: "Hello" },
1417/// ))
1418/// .exec(&mut db)
1419/// .await?;
1420/// // (User, Post)
1421/// # Ok(())
1422/// # }
1423/// ```
1424///
1425/// ## Mixed tuple
1426///
1427/// Typed batches and single creates can be mixed inside a tuple:
1428///
1429/// ```no_run
1430/// # #[derive(toasty::Model)]
1431/// # struct User {
1432/// #     #[key]
1433/// #     #[auto]
1434/// #     id: i64,
1435/// #     name: String,
1436/// # }
1437/// # #[derive(toasty::Model)]
1438/// # struct Post {
1439/// #     #[key]
1440/// #     #[auto]
1441/// #     id: i64,
1442/// #     title: String,
1443/// # }
1444/// # async fn example(mut db: toasty::Db) -> toasty::Result<()> {
1445/// let (users, post) = toasty::create!((
1446///     User::[ { name: "Alice" }, { name: "Bob" } ],
1447///     Post { title: "Hello" },
1448/// ))
1449/// .exec(&mut db)
1450/// .await?;
1451/// // (Vec<User>, Post)
1452/// # Ok(())
1453/// # }
1454/// ```
1455///
1456/// # Field values
1457///
1458/// ## Expressions
1459///
1460/// Any Rust expression is valid as a field value — literals, variables, and
1461/// function calls all work.
1462///
1463/// ```
1464/// # #[derive(toasty::Model)]
1465/// # struct User {
1466/// #     #[key]
1467/// #     #[auto]
1468/// #     id: i64,
1469/// #     name: String,
1470/// #     email: String,
1471/// # }
1472/// let name = "Alice";
1473/// let _ = toasty::create!(User { name: name, email: format!("{}@example.com", name) });
1474/// ```
1475///
1476/// ## Nested struct (BelongsTo / HasOne)
1477///
1478/// Use `{ ... }` **without** a type prefix to create a related record inline.
1479/// The macro expands the nested fields into a create builder and passes it
1480/// to the field's setter method.
1481///
1482/// ```
1483/// # #[derive(toasty::Model)]
1484/// # struct User {
1485/// #     #[key]
1486/// #     #[auto]
1487/// #     id: i64,
1488/// #     name: String,
1489/// # }
1490/// # #[derive(toasty::Model)]
1491/// # struct Todo {
1492/// #     #[key]
1493/// #     #[auto]
1494/// #     id: i64,
1495/// #     title: String,
1496/// #     #[index]
1497/// #     user_id: i64,
1498/// #     #[belongs_to(key = user_id, references = id)]
1499/// #     user: toasty::BelongsTo<User>,
1500/// # }
1501/// let _ = toasty::create!(Todo {
1502///     title: "buy milk",
1503///     user: { name: "Alice" }
1504/// });
1505/// // Expands to:
1506/// // Todo::create()
1507/// //     .title("buy milk")
1508/// //     .user(Todo::fields().user().create().name("Alice"))
1509/// ```
1510///
1511/// The related record is created first and the foreign key is set
1512/// automatically.
1513///
1514/// ## Nested list (HasMany)
1515///
1516/// Use `[{ ... }, { ... }]` to create multiple related records. The macro
1517/// expands each entry into a create builder and passes them as an array to
1518/// the plural field setter.
1519///
1520/// ```
1521/// # #[derive(toasty::Model)]
1522/// # struct User {
1523/// #     #[key]
1524/// #     #[auto]
1525/// #     id: i64,
1526/// #     name: String,
1527/// #     #[has_many]
1528/// #     todos: toasty::HasMany<Todo>,
1529/// # }
1530/// # #[derive(toasty::Model)]
1531/// # struct Todo {
1532/// #     #[key]
1533/// #     #[auto]
1534/// #     id: i64,
1535/// #     title: String,
1536/// #     #[index]
1537/// #     user_id: i64,
1538/// #     #[belongs_to(key = user_id, references = id)]
1539/// #     user: toasty::BelongsTo<User>,
1540/// # }
1541/// let _ = toasty::create!(User {
1542///     name: "Alice",
1543///     todos: [{ title: "first" }, { title: "second" }]
1544/// });
1545/// // Expands to:
1546/// // User::create()
1547/// //     .name("Alice")
1548/// //     .todos([
1549/// //         User::fields().todos().create().title("first"),
1550/// //         User::fields().todos().create().title("second"),
1551/// //     ])
1552/// ```
1553///
1554/// Items in a nested list can also be plain expressions (e.g., an existing
1555/// builder value).
1556///
1557/// ## Deep nesting
1558///
1559/// Nesting composes to arbitrary depth:
1560///
1561/// ```
1562/// # #[derive(toasty::Model)]
1563/// # struct User {
1564/// #     #[key]
1565/// #     #[auto]
1566/// #     id: i64,
1567/// #     name: String,
1568/// #     #[has_many]
1569/// #     todos: toasty::HasMany<Todo>,
1570/// # }
1571/// # #[derive(toasty::Model)]
1572/// # struct Todo {
1573/// #     #[key]
1574/// #     #[auto]
1575/// #     id: i64,
1576/// #     title: String,
1577/// #     #[index]
1578/// #     user_id: i64,
1579/// #     #[belongs_to(key = user_id, references = id)]
1580/// #     user: toasty::BelongsTo<User>,
1581/// #     #[has_many]
1582/// #     tags: toasty::HasMany<Tag>,
1583/// # }
1584/// # #[derive(toasty::Model)]
1585/// # struct Tag {
1586/// #     #[key]
1587/// #     #[auto]
1588/// #     id: i64,
1589/// #     name: String,
1590/// #     #[index]
1591/// #     todo_id: i64,
1592/// #     #[belongs_to(key = todo_id, references = id)]
1593/// #     todo: toasty::BelongsTo<Todo>,
1594/// # }
1595/// let _ = toasty::create!(User {
1596///     name: "Alice",
1597///     todos: [{
1598///         title: "task",
1599///         tags: [{ name: "urgent" }, { name: "work" }]
1600///     }]
1601/// });
1602/// ```
1603///
1604/// This creates a `User`, then a `Todo` linked to that user, then two `Tag`
1605/// records linked to that todo.
1606///
1607/// # Fields that can be omitted
1608///
1609/// | Field type | Behavior when omitted |
1610/// |---|---|
1611/// | `#[auto]` | Value generated by the database or Toasty |
1612/// | `Option<T>` | Defaults to `None` (`NULL`) |
1613/// | `#[default(expr)]` | Uses the default expression |
1614/// | `#[update(expr)]` | Uses the expression as the initial value |
1615/// | `HasMany<T>` | No related records created |
1616/// | `HasOne<Option<T>>` | No related record created |
1617/// | `BelongsTo<Option<T>>` | Foreign key set to `NULL` |
1618///
1619/// Required fields (`String`, `i64`, non-optional `BelongsTo`, etc.) that are
1620/// missing do not cause a compile-time error. The insert fails at runtime with
1621/// a database constraint violation.
1622///
1623/// # Compile errors
1624///
1625/// **Type prefix on nested struct:**
1626///
1627/// ```compile_fail
1628/// # #[derive(toasty::Model)]
1629/// # struct User {
1630/// #     #[key]
1631/// #     #[auto]
1632/// #     id: i64,
1633/// #     name: String,
1634/// # }
1635/// # #[derive(toasty::Model)]
1636/// # struct Todo {
1637/// #     #[key]
1638/// #     #[auto]
1639/// #     id: i64,
1640/// #     #[index]
1641/// #     user_id: i64,
1642/// #     #[belongs_to(key = user_id, references = id)]
1643/// #     user: toasty::BelongsTo<User>,
1644/// # }
1645/// // Error: remove the type prefix `User` — use `{ ... }` without a type name
1646/// toasty::create!(Todo { user: User { name: "Alice" } })
1647/// ```
1648///
1649/// Correct:
1650///
1651/// ```
1652/// # #[derive(toasty::Model)]
1653/// # struct User {
1654/// #     #[key]
1655/// #     #[auto]
1656/// #     id: i64,
1657/// #     name: String,
1658/// # }
1659/// # #[derive(toasty::Model)]
1660/// # struct Todo {
1661/// #     #[key]
1662/// #     #[auto]
1663/// #     id: i64,
1664/// #     #[index]
1665/// #     user_id: i64,
1666/// #     #[belongs_to(key = user_id, references = id)]
1667/// #     user: toasty::BelongsTo<User>,
1668/// # }
1669/// let _ = toasty::create!(Todo { user: { name: "Alice" } });
1670/// ```
1671///
1672/// Nested struct values infer their type from the field.
1673///
1674/// **Nested lists:**
1675///
1676/// ```compile_fail
1677/// # #[derive(toasty::Model)]
1678/// # struct User {
1679/// #     #[key]
1680/// #     #[auto]
1681/// #     id: i64,
1682/// #     field: String,
1683/// # }
1684/// // Error: nested lists are not supported in create!
1685/// toasty::create!(User { field: [[{ }]] })
1686/// ```
1687///
1688/// **Missing braces or batch bracket:**
1689///
1690/// ```compile_fail
1691/// # #[derive(toasty::Model)]
1692/// # struct User {
1693/// #     #[key]
1694/// #     #[auto]
1695/// #     id: i64,
1696/// # }
1697/// // Error: expected `{` for single creation or `::[` for batch creation after type path
1698/// toasty::create!(User)
1699/// ```
1700///
1701/// # Return type
1702///
1703/// | Form | Returns |
1704/// |---|---|
1705/// | `Type { ... }` | `TypeCreate` (single builder) |
1706/// | `in expr { ... }` | Builder for the relation's model |
1707/// | `Type::[ ... ]` | `Batch` — executes to `Vec<Type>` |
1708/// | `( ... )` | `Batch` — executes to tuple of results |
1709///
1710/// Single and scoped forms return a builder — call `.exec(&mut db).await?`.
1711/// Batch and tuple forms return a `Batch` — also call `.exec(&mut db).await?`.
1712#[proc_macro]
1713pub fn create(input: TokenStream) -> TokenStream {
1714    match create::generate(input.into()) {
1715        Ok(output) => output.into(),
1716        Err(e) => e.to_compile_error().into(),
1717    }
1718}