queryweave-sql 0.2.1

The SQL runtime of queryweave: database-agnostic sqlx query building (WHERE, ORDER BY, LIMIT/OFFSET, SET) from the data queryweave's derives generate.
Documentation
//! The `queryweave` runtime for the persistence layer: the traits and SQL the
//! code generated for entities and wrappers (`Columns`, `AppendToQuery`,
//! `AppendConditions`) names, on top of the core runtime
//! (`queryweave`), which it re-exports so that code names only this crate.
//!
//! This is where the domain's database-agnostic filters, sorting and writes
//! (`queryweave::FilterCondition`, `queryweave::SortKey`,
//! `queryweave::FieldValue`) become SQL. They name domain fields; the
//! [`Columns`] of the entity storing them say which column each lives in, and
//! write the SQL against them, into the query being built: any type
//! implementing the target traits ([`ConditionsTarget`], [`OrderByTarget`],
//! [`PageTarget`], and `AssignmentsTarget` with merge patches).
//!
//! # Features
//!
//! - `sqlx` (default): sqlx's `QueryBuilder` is a target, for any database
//!   whose driver binds the domain's raw values (`BindValues`).
//! - `sea-query`: sea-query's `SelectStatement` (conditions, `ORDER BY`, page),
//!   `UpdateStatement` (conditions, assignments) and `DeleteStatement`
//!   (conditions) are targets. With `sea-orm`, write into a query's
//!   statement through `QueryTrait::query`:
//!   `Order::push_conditions(select.query(), …)`.
//! - `diesel`: the `diesel` module, the typed conditions, orderings,
//!   page and assignments of an entity's Diesel columns, for the SQL types
//!   every backend has; `diesel-postgres`, `diesel-mysql` and `diesel-sqlite`
//!   add each backend's own date-time types.
//! - `merge-patch` (default): merge-patch writes (`push_assignments`).
//!
//! The SQL is portable across Postgres, MySQL/MariaDB and SQLite. What stays
//! database-specific is semantics, not syntax: `LIKE` is case-sensitive on
//! Postgres but case-insensitive on SQLite (for ASCII) and under MySQL's
//! default collations, and column names are written unquoted, so they must not
//! be reserved words of the database.

mod backend;
mod columns;
mod sql;
mod target;

pub use queryweave::*;

#[cfg(feature = "sqlx")]
pub use backend::BindValues;
#[cfg(feature = "diesel")]
pub use backend::diesel;
#[cfg(feature = "sea-query")]
pub use backend::sea_query;
pub use columns::{
    Columns, Message, column_for, first_field_without_column, missing_column_message,
};
pub use sql::{
    LIKE_ESCAPE, escape_like, like_pattern, page_offset, push_page, sql_direction, sql_junction,
    sql_operator,
};
#[cfg(feature = "merge-patch")]
pub use target::AssignmentsTarget;
pub use target::{ConditionsTarget, Junction, OrderByTarget, PageTarget};

/// The sqlx query builder the clauses are appended to, and the separated
/// builder `Columns::push_assignments` returns for it: re-exported so callers
/// can name the same sqlx as this crate, whatever they call their own sqlx
/// dependency.
#[cfg(feature = "sqlx")]
pub use sqlx::{QueryBuilder, query_builder::Separated};

/// Not public API: what the code generated by `queryweave-derive`
/// calls.
///
/// The inherent methods `#[derive(Columns)]` emits run the column check
/// themselves (so a missing column is reported at *their* caller, the line
/// writing the data, rather than inside the derive) and then call the target's
/// unchecked `write_*` method, so the check is not reported twice.
#[doc(hidden)]
pub mod __private {
    pub use crate::__if_diesel as if_diesel;
    pub use crate::__if_merge_patch as if_merge_patch;
    #[cfg(feature = "diesel")]
    pub use crate::backend::diesel::check::{assert_assignment_types, assert_condition_types};
    pub use crate::columns::assert_model_columns;
    pub use queryweave::__private::*;
}

/// Not public API: the code `#[derive(Columns)]` generates for a Diesel
/// entity, let through only when this crate has its `diesel` feature.
///
/// The derive cannot see this crate's features, and a `#[cfg]` in its output
/// would test the user's crate's; this macro is defined by this crate, once
/// per state of the feature, so the generated Diesel code (which names this
/// crate's `diesel` module) is dropped before it is checked when the feature
/// is off, and an entity also used with another backend costs nothing.
#[cfg(feature = "diesel")]
#[doc(hidden)]
#[macro_export]
macro_rules! __if_diesel {
    ($($items:tt)*) => {
        $($items)*
    };
}

/// See the other definition: without the `diesel` feature, nothing.
#[cfg(not(feature = "diesel"))]
#[doc(hidden)]
#[macro_export]
macro_rules! __if_diesel {
    ($($items:tt)*) => {};
}

/// Not public API: the merge-patch writes `#[derive(Columns)]` generates
/// (`push_assignments`, and a Diesel entity's `diesel_changeset`), let
/// through only when this crate has its `merge-patch` feature.
///
/// They name this crate's merge-patch items (`AssignmentsTarget`,
/// `FieldValues`), which exist only with the feature; the derive's own
/// `merge-patch` feature is another crate's, so it cannot tell. Like
/// `__if_diesel`, defined once per state of the feature.
#[cfg(feature = "merge-patch")]
#[doc(hidden)]
#[macro_export]
macro_rules! __if_merge_patch {
    ($($items:tt)*) => {
        $($items)*
    };
}

/// See the other definition: without the `merge-patch` feature, nothing.
#[cfg(not(feature = "merge-patch"))]
#[doc(hidden)]
#[macro_export]
macro_rules! __if_merge_patch {
    ($($items:tt)*) => {};
}

/// Append the filters query to the parent query.
pub trait AppendToQuery {
    /// Append the filters query to the parent query: any target taking
    /// conditions, an `ORDER BY` and a page (for sqlx, a `QueryBuilder` of any
    /// `BindValues` database).
    fn append_to_query<Q, F>(&self, query: &mut Q, privileged_guard_option: Option<F>)
    where
        Q: ConditionsTarget + OrderByTarget + PageTarget,
        F: FnOnce(&mut Q, &Self);
}

/// Append a filter-options model's set filters to a query as conditions.
pub trait AppendConditions {
    /// Append the set filters as conditions joined by `junction`, to any
    /// target taking conditions (for sqlx, a `QueryBuilder` of any
    /// `BindValues` database).
    fn append_conditions<Q: ConditionsTarget>(&self, query: &mut Q, junction: Junction);
}