Skip to main content

a3s_orm/
lib.rs

1//! Type-safe SQL query building inspired by Kysely.
2//!
3//! `a3s-orm` keeps schema typing, query construction, SQL compilation, and
4//! execution behind separate interfaces. It does not use an Active Record
5//! model and never performs implicit runtime value conversion.
6
7#![cfg_attr(feature = "sqlite", doc = include_str!("../README.md"))]
8
9mod ast;
10pub mod compiler;
11pub mod decode;
12pub mod drivers;
13pub mod error;
14pub mod executor;
15pub mod expression;
16pub mod function;
17pub mod migration;
18pub mod query;
19pub mod schema;
20pub mod value;
21pub mod window;
22
23pub use compiler::{CompiledQuery, Dialect, MysqlDialect, PostgresDialect, SqliteDialect};
24pub use decode::{DecodeError, FromRow, FromValue, Row};
25#[cfg(feature = "postgres")]
26pub use drivers::postgres::{
27    PostgresError, PostgresExecutor, PostgresIsolationLevel, PostgresMigrationError,
28    PostgresMigrationOptions, PostgresOptionsError, PostgresPoolHealth,
29    PostgresPoolMetricsSnapshot, PostgresPoolOptions, PostgresPoolStatus, PostgresRetryClass,
30    PostgresRow, PostgresTlsError, PostgresTlsOptions, PostgresTransaction,
31    PostgresTransactionAccessMode, PostgresTransactionError, PostgresTransactionOptions,
32};
33#[cfg(feature = "sqlite")]
34pub use drivers::sqlite::{
35    SqliteError, SqliteExecutor, SqliteJournalMode, SqliteMigrationError, SqliteOptions, SqliteRow,
36    SqliteSavepoint, SqliteSavepointError, SqliteTransaction, SqliteTransactionError,
37};
38pub use error::{Error, Result};
39pub use executor::{
40    Database, DatabaseError, ExecuteResult, Executor, QueryResult, Transaction, TransactionManager,
41};
42pub use expression::{
43    exists, not, Column, Expression, OrderDirection, SelectionExt, SqlComparable, SqlNumeric,
44    WindowBoundary, WindowFrame, WindowFrameUnits,
45};
46pub use function::{
47    bound, cast, coalesce, count, count_all, least, max, min, scalar_subquery, sql_function,
48    TypedExpression,
49};
50pub use migration::{
51    pending_migrations, AppliedMigration, Migration, MigrationBackend, MigrationError,
52    MigrationReport, Migrator, PreparedMigration,
53};
54pub use query::{
55    delete_from, insert_into, lock_table, select_from, select_from_as, sql_query, update_table,
56    ConflictTarget, InsertRow, PostgresTableLockMode, Query, SqlQuery, TableLockQuery,
57};
58pub use schema::{Table, TableRef};
59pub use value::{IntoSqlValue, SqlArray, Value};
60pub use window::{dense_rank, rank, row_number, WindowExpression};
61
62/// Define a typed table marker and its columns.
63///
64/// ```
65/// use a3s_orm::orm_table;
66///
67/// orm_table! {
68///     pub struct Person => "person" {
69///         id: i64 => "id",
70///         name: String => "name",
71///     }
72/// }
73/// ```
74///
75/// Column values are checked against the schema type:
76///
77/// ```compile_fail
78/// use a3s_orm::{insert_into, orm_table};
79///
80/// orm_table! {
81///     struct Person => "person" {
82///         age: i32 => "age",
83///     }
84/// }
85///
86/// let _ = insert_into::<Person>().value(Person::age(), "not an integer");
87/// ```
88///
89/// Assignments cannot use a column owned by another table:
90///
91/// ```compile_fail
92/// use a3s_orm::{orm_table, update_table};
93///
94/// orm_table! { struct Person => "person" { name: String => "name" } }
95/// orm_table! { struct Pet => "pet" { name: String => "name" } }
96///
97/// let _ = update_table::<Person>().set(Pet::name(), "wrong table");
98/// ```
99///
100/// Expression assignments preserve the column's declared value family:
101///
102/// ```compile_fail
103/// use a3s_orm::{bound, orm_table, update_table};
104///
105/// orm_table! { struct Person => "person" { age: i32 => "age" } }
106///
107/// let _ = update_table::<Person>()
108///     .set_expression(Person::age(), bound::<String>("wrong type"));
109/// ```
110///
111/// Column comparisons preserve the declared SQL value family, including
112/// nullable and non-nullable forms of the same base type:
113///
114/// ```compile_fail
115/// use a3s_orm::orm_table;
116///
117/// orm_table! { struct Person => "person" { id: i64 => "id" } }
118/// orm_table! { struct Pet => "pet" { name: String => "name" } }
119///
120/// let _ = Person::id().eq_column(Pet::name());
121/// ```
122#[macro_export]
123macro_rules! orm_table {
124    (
125        $(#[$table_meta:meta])*
126        $visibility:vis struct $table:ident => $table_name:literal {
127            $(
128                $(#[$column_meta:meta])*
129                $column:ident : $value:ty => $column_name:literal
130            ),* $(,)?
131        }
132    ) => {
133        $(#[$table_meta])*
134        #[derive(Debug, Clone, Copy, Default)]
135        $visibility struct $table;
136
137        impl $crate::Table for $table {
138            const NAME: &'static str = $table_name;
139        }
140
141        impl $table {
142            $(
143                $(#[$column_meta])*
144                $visibility const fn $column() -> $crate::Column<$table, $value> {
145                    $crate::Column::new($table_name, $column_name)
146                }
147            )*
148        }
149    };
150}