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/// ## Field syntax
1288///
1289/// Fields inside `{ ... }` can use either explicit or shorthand syntax:
1290///
1291/// - **Explicit:** `field: expr` — sets the field to the given expression.
1292/// - **Shorthand:** `field` — equivalent to `field: field`, using a variable
1293/// with the same name as the field.
1294///
1295/// These can be mixed freely, just like Rust struct literals:
1296///
1297/// ```ignore
1298/// let name = "Alice".to_string();
1299/// toasty::create!(User { name, email: "alice@example.com" })
1300/// ```
1301///
1302/// ## Single creation
1303///
1304/// ```ignore
1305/// toasty::create!(Type { field: value, ... })
1306/// ```
1307///
1308/// Expands to `Type::create().field(value)...` and returns the model's create
1309/// builder (e.g., `UserCreate`).
1310///
1311/// ```no_run
1312/// # #[derive(toasty::Model)]
1313/// # struct User {
1314/// # #[key]
1315/// # #[auto]
1316/// # id: i64,
1317/// # name: String,
1318/// # email: String,
1319/// # }
1320/// # async fn example(mut db: toasty::Db) -> toasty::Result<()> {
1321/// let user = toasty::create!(User {
1322/// name: "Alice",
1323/// email: "alice@example.com"
1324/// })
1325/// .exec(&mut db)
1326/// .await?;
1327/// # Ok(())
1328/// # }
1329/// ```
1330///
1331/// ## Scoped creation
1332///
1333/// ```ignore
1334/// toasty::create!(in expr { field: value, ... })
1335/// ```
1336///
1337/// Expands to `expr.create().field(value)...`. Creates a record through a
1338/// relation accessor. The foreign key is set automatically.
1339///
1340/// ```no_run
1341/// # #[derive(toasty::Model)]
1342/// # struct User {
1343/// # #[key]
1344/// # #[auto]
1345/// # id: i64,
1346/// # name: String,
1347/// # #[has_many]
1348/// # todos: toasty::HasMany<Todo>,
1349/// # }
1350/// # #[derive(toasty::Model)]
1351/// # struct Todo {
1352/// # #[key]
1353/// # #[auto]
1354/// # id: i64,
1355/// # title: String,
1356/// # #[index]
1357/// # user_id: i64,
1358/// # #[belongs_to(key = user_id, references = id)]
1359/// # user: toasty::BelongsTo<User>,
1360/// # }
1361/// # async fn example(mut db: toasty::Db, user: User) -> toasty::Result<()> {
1362/// let todo = toasty::create!(in user.todos() { title: "buy milk" })
1363/// .exec(&mut db)
1364/// .await?;
1365///
1366/// // todo.user_id == user.id
1367/// # Ok(())
1368/// # }
1369/// ```
1370///
1371/// ## Typed batch
1372///
1373/// ```ignore
1374/// toasty::create!(Type::[ { fields }, { fields }, ... ])
1375/// ```
1376///
1377/// Expands to `toasty::batch([builder1, builder2, ...])` and returns
1378/// `Vec<Type>` when executed:
1379///
1380/// ```no_run
1381/// # #[derive(toasty::Model)]
1382/// # struct User {
1383/// # #[key]
1384/// # #[auto]
1385/// # id: i64,
1386/// # name: String,
1387/// # }
1388/// # async fn example(mut db: toasty::Db) -> toasty::Result<()> {
1389/// let users = toasty::create!(User::[
1390/// { name: "Alice" },
1391/// { name: "Bob" },
1392/// ])
1393/// .exec(&mut db)
1394/// .await?;
1395/// // users: Vec<User>
1396/// # Ok(())
1397/// # }
1398/// ```
1399///
1400/// ## Tuple
1401///
1402/// ```ignore
1403/// toasty::create!((
1404/// Type1 { fields },
1405/// Type2 { fields },
1406/// ...
1407/// ))
1408/// ```
1409///
1410/// Expands to `toasty::batch((builder1, builder2, ...))` and returns a
1411/// tuple matching the input types:
1412///
1413/// ```no_run
1414/// # #[derive(toasty::Model)]
1415/// # struct User {
1416/// # #[key]
1417/// # #[auto]
1418/// # id: i64,
1419/// # name: String,
1420/// # }
1421/// # #[derive(toasty::Model)]
1422/// # struct Post {
1423/// # #[key]
1424/// # #[auto]
1425/// # id: i64,
1426/// # title: String,
1427/// # }
1428/// # async fn example(mut db: toasty::Db) -> toasty::Result<()> {
1429/// let (user, post) = toasty::create!((
1430/// User { name: "Alice" },
1431/// Post { title: "Hello" },
1432/// ))
1433/// .exec(&mut db)
1434/// .await?;
1435/// // (User, Post)
1436/// # Ok(())
1437/// # }
1438/// ```
1439///
1440/// ## Mixed tuple
1441///
1442/// Typed batches and single creates can be mixed inside a tuple:
1443///
1444/// ```no_run
1445/// # #[derive(toasty::Model)]
1446/// # struct User {
1447/// # #[key]
1448/// # #[auto]
1449/// # id: i64,
1450/// # name: String,
1451/// # }
1452/// # #[derive(toasty::Model)]
1453/// # struct Post {
1454/// # #[key]
1455/// # #[auto]
1456/// # id: i64,
1457/// # title: String,
1458/// # }
1459/// # async fn example(mut db: toasty::Db) -> toasty::Result<()> {
1460/// let (users, post) = toasty::create!((
1461/// User::[ { name: "Alice" }, { name: "Bob" } ],
1462/// Post { title: "Hello" },
1463/// ))
1464/// .exec(&mut db)
1465/// .await?;
1466/// // (Vec<User>, Post)
1467/// # Ok(())
1468/// # }
1469/// ```
1470///
1471/// # Field values
1472///
1473/// ## Expressions
1474///
1475/// Any Rust expression is valid as a field value — literals, variables, and
1476/// function calls all work. When a variable has the same name as the field,
1477/// you can use the shorthand syntax (just `name` instead of `name: name`):
1478///
1479/// ```
1480/// # #[derive(toasty::Model)]
1481/// # struct User {
1482/// # #[key]
1483/// # #[auto]
1484/// # id: i64,
1485/// # name: String,
1486/// # email: String,
1487/// # }
1488/// let name = "Alice";
1489/// let _ = toasty::create!(User { name, email: format!("{}@example.com", name) });
1490/// ```
1491///
1492/// When the variable name differs from the field name, use the explicit
1493/// `field: expr` form:
1494///
1495/// ```
1496/// # #[derive(toasty::Model)]
1497/// # struct User {
1498/// # #[key]
1499/// # #[auto]
1500/// # id: i64,
1501/// # name: String,
1502/// # }
1503/// let user_name = "Alice";
1504/// let _ = toasty::create!(User { name: user_name });
1505/// ```
1506///
1507/// ## Nested struct (BelongsTo / HasOne)
1508///
1509/// Use `{ ... }` **without** a type prefix to create a related record inline.
1510/// The macro expands the nested fields into a create builder and passes it
1511/// to the field's setter method.
1512///
1513/// ```
1514/// # #[derive(toasty::Model)]
1515/// # struct User {
1516/// # #[key]
1517/// # #[auto]
1518/// # id: i64,
1519/// # name: String,
1520/// # }
1521/// # #[derive(toasty::Model)]
1522/// # struct Todo {
1523/// # #[key]
1524/// # #[auto]
1525/// # id: i64,
1526/// # title: String,
1527/// # #[index]
1528/// # user_id: i64,
1529/// # #[belongs_to(key = user_id, references = id)]
1530/// # user: toasty::BelongsTo<User>,
1531/// # }
1532/// let _ = toasty::create!(Todo {
1533/// title: "buy milk",
1534/// user: { name: "Alice" }
1535/// });
1536/// // Expands to:
1537/// // Todo::create()
1538/// // .title("buy milk")
1539/// // .user(Todo::fields().user().create().name("Alice"))
1540/// ```
1541///
1542/// The related record is created first and the foreign key is set
1543/// automatically.
1544///
1545/// ## Nested list (HasMany)
1546///
1547/// Use `[{ ... }, { ... }]` to create multiple related records. The macro
1548/// expands each entry into a create builder and passes them as an array to
1549/// the plural field setter.
1550///
1551/// ```
1552/// # #[derive(toasty::Model)]
1553/// # struct User {
1554/// # #[key]
1555/// # #[auto]
1556/// # id: i64,
1557/// # name: String,
1558/// # #[has_many]
1559/// # todos: toasty::HasMany<Todo>,
1560/// # }
1561/// # #[derive(toasty::Model)]
1562/// # struct Todo {
1563/// # #[key]
1564/// # #[auto]
1565/// # id: i64,
1566/// # title: String,
1567/// # #[index]
1568/// # user_id: i64,
1569/// # #[belongs_to(key = user_id, references = id)]
1570/// # user: toasty::BelongsTo<User>,
1571/// # }
1572/// let _ = toasty::create!(User {
1573/// name: "Alice",
1574/// todos: [{ title: "first" }, { title: "second" }]
1575/// });
1576/// // Expands to:
1577/// // User::create()
1578/// // .name("Alice")
1579/// // .todos([
1580/// // User::fields().todos().create().title("first"),
1581/// // User::fields().todos().create().title("second"),
1582/// // ])
1583/// ```
1584///
1585/// Items in a nested list can also be plain expressions (e.g., an existing
1586/// builder value).
1587///
1588/// ## Deep nesting
1589///
1590/// Nesting composes to arbitrary depth:
1591///
1592/// ```
1593/// # #[derive(toasty::Model)]
1594/// # struct User {
1595/// # #[key]
1596/// # #[auto]
1597/// # id: i64,
1598/// # name: String,
1599/// # #[has_many]
1600/// # todos: toasty::HasMany<Todo>,
1601/// # }
1602/// # #[derive(toasty::Model)]
1603/// # struct Todo {
1604/// # #[key]
1605/// # #[auto]
1606/// # id: i64,
1607/// # title: String,
1608/// # #[index]
1609/// # user_id: i64,
1610/// # #[belongs_to(key = user_id, references = id)]
1611/// # user: toasty::BelongsTo<User>,
1612/// # #[has_many]
1613/// # tags: toasty::HasMany<Tag>,
1614/// # }
1615/// # #[derive(toasty::Model)]
1616/// # struct Tag {
1617/// # #[key]
1618/// # #[auto]
1619/// # id: i64,
1620/// # name: String,
1621/// # #[index]
1622/// # todo_id: i64,
1623/// # #[belongs_to(key = todo_id, references = id)]
1624/// # todo: toasty::BelongsTo<Todo>,
1625/// # }
1626/// let _ = toasty::create!(User {
1627/// name: "Alice",
1628/// todos: [{
1629/// title: "task",
1630/// tags: [{ name: "urgent" }, { name: "work" }]
1631/// }]
1632/// });
1633/// ```
1634///
1635/// This creates a `User`, then a `Todo` linked to that user, then two `Tag`
1636/// records linked to that todo.
1637///
1638/// # Fields that can be omitted
1639///
1640/// | Field type | Behavior when omitted |
1641/// |---|---|
1642/// | `#[auto]` | Value generated by the database or Toasty |
1643/// | `Option<T>` | Defaults to `None` (`NULL`) |
1644/// | `#[default(expr)]` | Uses the default expression |
1645/// | `#[update(expr)]` | Uses the expression as the initial value |
1646/// | `HasMany<T>` | No related records created |
1647/// | `HasOne<Option<T>>` | No related record created |
1648/// | `BelongsTo<Option<T>>` | Foreign key set to `NULL` |
1649///
1650/// Required fields (`String`, `i64`, non-optional `BelongsTo`, etc.) that are
1651/// missing do not cause a compile-time error. The insert fails at runtime with
1652/// a database constraint violation.
1653///
1654/// # Compile errors
1655///
1656/// **Type prefix on nested struct:**
1657///
1658/// ```compile_fail
1659/// # #[derive(toasty::Model)]
1660/// # struct User {
1661/// # #[key]
1662/// # #[auto]
1663/// # id: i64,
1664/// # name: String,
1665/// # }
1666/// # #[derive(toasty::Model)]
1667/// # struct Todo {
1668/// # #[key]
1669/// # #[auto]
1670/// # id: i64,
1671/// # #[index]
1672/// # user_id: i64,
1673/// # #[belongs_to(key = user_id, references = id)]
1674/// # user: toasty::BelongsTo<User>,
1675/// # }
1676/// // Error: remove the type prefix `User` — use `{ ... }` without a type name
1677/// toasty::create!(Todo { user: User { name: "Alice" } })
1678/// ```
1679///
1680/// Correct:
1681///
1682/// ```
1683/// # #[derive(toasty::Model)]
1684/// # struct User {
1685/// # #[key]
1686/// # #[auto]
1687/// # id: i64,
1688/// # name: String,
1689/// # }
1690/// # #[derive(toasty::Model)]
1691/// # struct Todo {
1692/// # #[key]
1693/// # #[auto]
1694/// # id: i64,
1695/// # #[index]
1696/// # user_id: i64,
1697/// # #[belongs_to(key = user_id, references = id)]
1698/// # user: toasty::BelongsTo<User>,
1699/// # }
1700/// let _ = toasty::create!(Todo { user: { name: "Alice" } });
1701/// ```
1702///
1703/// Nested struct values infer their type from the field.
1704///
1705/// **Nested lists:**
1706///
1707/// ```compile_fail
1708/// # #[derive(toasty::Model)]
1709/// # struct User {
1710/// # #[key]
1711/// # #[auto]
1712/// # id: i64,
1713/// # field: String,
1714/// # }
1715/// // Error: nested lists are not supported in create!
1716/// toasty::create!(User { field: [[{ }]] })
1717/// ```
1718///
1719/// **Missing braces or batch bracket:**
1720///
1721/// ```compile_fail
1722/// # #[derive(toasty::Model)]
1723/// # struct User {
1724/// # #[key]
1725/// # #[auto]
1726/// # id: i64,
1727/// # }
1728/// // Error: expected `{` for single creation or `::[` for batch creation after type path
1729/// toasty::create!(User)
1730/// ```
1731///
1732/// # Return type
1733///
1734/// | Form | Returns |
1735/// |---|---|
1736/// | `Type { ... }` | `TypeCreate` (single builder) |
1737/// | `in expr { ... }` | Builder for the relation's model |
1738/// | `Type::[ ... ]` | `Batch` — executes to `Vec<Type>` |
1739/// | `( ... )` | `Batch` — executes to tuple of results |
1740///
1741/// Single and scoped forms return a builder — call `.exec(&mut db).await?`.
1742/// Batch and tuple forms return a `Batch` — also call `.exec(&mut db).await?`.
1743#[proc_macro]
1744pub fn create(input: TokenStream) -> TokenStream {
1745 match create::generate(input.into()) {
1746 Ok(output) => output.into(),
1747 Err(e) => e.to_compile_error().into(),
1748 }
1749}