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