keelson_core/lib.rs
1//! Core primitives for keelson.
2//!
3//! Everything else — the three dialect crates, the generated models, the backend
4//! adapters — is built out of what is defined here:
5//!
6//! - [`Value`], the bound argument. keelson carries its own value enum instead of
7//! being generic over a driver's parameter type, which keeps [`Expression`] free
8//! of type parameters and makes a built query's arguments inspectable.
9//! - [`Dialect`], the per-database syntax decisions, and nothing else.
10//! - [`Expression`], a fragment that can render itself, and [`SqlWriter`], which
11//! owns the SQL buffer, the argument list and the placeholder counter together
12//! so that nesting re-indexes for free.
13//! - [`Mod`], the composition unit: a tuple of mods is a mod, and so is
14//! `Option<M>`, `Vec<M>` and `[M; N]`.
15//! - [`Query`] and [`QueryType`], the little a runnable statement owes the layers
16//! above.
17//!
18//! Two properties are worth stating up front because the rest of the design leans
19//! on them. Rendering is **infallible** — `write_sql` returns nothing, and the one
20//! genuine failure (a named argument asked of a dialect without them) is recorded
21//! on the writer and surfaced once, by [`build`]. And **no public type carries a
22//! lifetime parameter**: identifiers and raw SQL are stored as
23//! `Cow<'static, str>`, so a query type is `SelectQuery`, never `SelectQuery<'a>`.
24//! The only lifetime in this crate is the transient one on [`SqlWriter`], which
25//! borrows the dialect for the duration of a single build.
26//!
27//! Building is entirely synchronous and driver-independent: it produces a `String`
28//! and a `Vec<Value>` and nothing more.
29//!
30//! ```
31//! # use keelson_core::{Dialect, Expression, SqlWriter, Value, build};
32//! #[derive(Debug)]
33//! struct AgeAtLeast(i32);
34//!
35//! impl Expression for AgeAtLeast {
36//! fn write_sql(&self, w: &mut SqlWriter<'_>) {
37//! w.push_str("(");
38//! w.push_quoted(&["age"]);
39//! w.push_str(" >= ");
40//! w.push_arg(self.0);
41//! w.push_str(")");
42//! }
43//! }
44//!
45//! # #[derive(Debug)]
46//! # struct Psql;
47//! # impl Dialect for Psql {
48//! # fn write_arg(&self, w: &mut SqlWriter<'_>, position: usize) {
49//! # w.push_str("$");
50//! # w.push_str(&position.to_string());
51//! # }
52//! # fn write_quoted(&self, w: &mut SqlWriter<'_>, s: &str) {
53//! # w.push_str("\"");
54//! # w.push_str(s);
55//! # w.push_str("\"");
56//! # }
57//! # }
58//! let (sql, args) = build(&Psql, &AgeAtLeast(21))?;
59//! assert_eq!(sql, r#"("age" >= $1)"#);
60//! assert_eq!(args, vec![Value::I32(21)]);
61//! # Ok::<_, keelson_core::Error>(())
62//! ```
63//!
64//! # Where this sits
65//!
66//! Layer 0 of keelson: the vocabulary, and the only crate every other one
67//! depends on. Above it sit the three Layer 1 dialects
68//! ([keelson-psql](https://docs.rs/keelson-psql), [keelson-mysql](https://docs.rs/keelson-mysql),
69//! [keelson-sqlite](https://docs.rs/keelson-sqlite)), which is where a user starts —
70//! a statement type comes from a dialect, never from here. Layer 2
71//! ([keelson-exec](https://docs.rs/keelson-exec)) runs what [`build`] returns, and Layer 3
72//! ([keelson-models](https://docs.rs/keelson-models)) is written against the
73//! [`clause`] traits defined here. The whole map, and one dependency line for
74//! it, is the [keelson](https://docs.rs/keelson) facade crate.
75#![warn(missing_docs)]
76
77pub mod clause;
78mod dialect;
79mod error;
80pub mod expr;
81mod mods;
82mod query;
83mod value;
84mod writer;
85
86pub use dialect::Dialect;
87pub use error::{Error, Result};
88pub use mods::{BuildMod, Mod, ModFn, mod_fn};
89pub use query::{Query, QueryExtensions, QueryType, RawQuery};
90pub use value::{CustomValue, FromValue, ToValue, Value, from_value_array};
91
92/// The derive macros, re-exported behind the `macros` feature.
93///
94/// [`Bind`](macro@Bind) writes the [`ToValue`]/[`FromValue`] pair for a
95/// newtype — the bound a keelson-gen column override must satisfy.
96/// [`FromRow`](macro@FromRow) maps a result row onto a struct; the trait it
97/// implements lives in keelson-exec, which any user of it already depends on.
98/// Both are documented in the keelson-macros crate.
99///
100/// `keelson_core::Bind` is the derive, `keelson_exec::Bind` the trait: two
101/// namespaces, so importing both is fine.
102#[cfg(feature = "macros")]
103pub use keelson_macros::{Bind, FromRow};
104
105/// The scanner each dialect's `sql!` forwards to. Not called directly.
106#[cfg(feature = "macros")]
107#[doc(hidden)]
108pub use keelson_macros::sql_with as __sql_with;
109pub use writer::{DynExpr, ExprFn, Expression, SqlWriter, build, build_from, dyn_expr, expr_fn};
110
111/// Stand-in dialects for tests. See [`dialect::testing`].
112#[cfg(any(test, feature = "testing"))]
113pub use dialect::testing;