1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
pub use butane_codegen::{butane_type, dataresult, model, FieldType};
pub use butane_core::custom;
pub use butane_core::fkey::ForeignKey;
pub use butane_core::many::Many;
pub use butane_core::migrations;
pub use butane_core::query;
pub use butane_core::{
AsPrimaryKey, DataObject, DataResult, Error, FieldType, FromSql, ObjectState, Result, SqlType,
SqlVal, SqlValRef, ToSql,
};
pub mod db {
pub use butane_core::db::*;
}
/// Macro to construct a [`BoolExpr`] (for use with a [`Query`]) from
/// an expression with Rust syntax.
///
/// Using this macro instead of constructing a `BoolExpr` has two
/// advantages:
/// 1. It will generally be more ergonomic
/// 2. References to nonexistent fields or type mismatches
/// (e.g. comparing a number to a string) will generate a compilation error
///
/// Usage: `filter!(Foo, expr)` where `Foo` is a model type (with the
/// `#[model]` attribute applied) and `expr` is a Rust-like expression
/// with a boolean value. `Foo`'s fields may be referred to as if they
/// were variables.
///
/// # Rust values
/// To refer to values from the surrounding rust function, enclose
/// them in braces, like `filter!(Foo, bar == {bar})`
///
/// # Function-like operations
/// Filters support some operations for which Rust does not have operators and which are instead
/// represented syntactically as function calls.
/// * `like`: parameter is a SQL LIKE expression string, e.g. `title.like("M%").
/// * `matches`: Parameter is a sub-expression. Use with a
/// [`ForeignKey`] field to evaluate as true if the referent
/// matches. For example, to find all posts made in blogs by people
/// named "Pete" we might say `filter!(Post, `blog.matches(author == "Pete"))`.
/// * `contains`: Essentially the many-to-many version of `matches`.
/// Parameter is a sub-expression. Use with a [`Many`]
/// field to evaluate as true if one of the many referents matches
/// the given expression. For example, in a blog post model with a field
/// `tags: Many<Tag>` we could filter to posts with a "cats" with
/// the following `tags.contains(tag == "cats"). If the expression
/// is single literal, it is assumed to be used to match the
/// primary key.
///
/// # Examples
/// ```
/// # use butane::query::BoolExpr;
/// # use butane_codegen::model;
/// # use butane_codegen::filter;
/// #[model]
/// struct Contestant {
/// #[pk]
/// name: String,
/// rank: i32,
/// nationality: String
/// }
/// let e: BoolExpr = filter!(Contestant, nationality == "US" && rank < 42);
/// let first_place = 1;
/// let e2 = filter!(Contestant, rank == { first_place });
/// let e3 = filter!(Contestant, name.like("A%"));
///```
///
/// [`BoolExpr`]: crate::query::BoolExpr
/// [`Query`]: crate::query::Query
pub use butane_codegen::filter;
/// Constructs a filtered database query.
///
/// Use as `query!(Foo, expr)`, where `Foo` is a model type. Returns [`Query`]`<Foo>`.
///
/// Shorthand for `Foo::query().filter(`[`filter`]`!(Foo, expr))`
//
/// # Examples
/// ```
/// # use butane::query::*;
/// # use butane_codegen::model;
/// # use butane::query;
/// # use butane::prelude::*;
/// #[model]
/// struct Contestant {
/// #[pk]
/// name: String,
/// rank: i32,
/// nationality: String
/// }
/// let top_tier: Query<Contestant> = query!(Contestant, rank <= 10);
///```
///
/// [`filter]: crate::filter
/// [`Query`]: crate::query::Query
#[macro_export]
macro_rules! query {
($model:ident, $filter:expr) => {
<$model as butane::DataResult>::query().filter(butane::filter!($model, $filter))
};
}
/// Type-safe way to refer to a column name. Use as
/// `colname!(MODEL_TYPE, FIELD_NAME)`. E.g. For a model type `Foo`
/// with a field `bar`, `colname!(Foo, bar) would return `"bar"`, but
/// `colname!(Foo, bat)` would be a compiler error (assuming `Foo`
/// does not have such a field.
#[macro_export]
macro_rules! colname {
($model:ident, $col:ident) => {
$model::fields().$col().name()
};
}
/// Finds a specific database object.
///
/// Use as `find!(Foo, expr, conn)`, where `Foo` is a model type and
/// conn implements `ConnectionImpl`. Returns
/// [`Result`]`<`Foo`>`. The error will be [`NoSuchObject`] if no
/// object was found. If more than one object matches the expression,
/// the first one found is returned.
///
/// This macro is for convenience -- it does nothing that can't be done with `query!` or `filter!`.
///
/// # Examples
/// ```no_run
/// # use butane::db::ConnectionSpec;
/// # use butane::query::BoolExpr;
/// # use butane_codegen::model;
/// # use butane::prelude::*;
/// # use butane::query;
/// # use butane::find;
/// # use butane::DataObject;
/// #[model]
/// struct Contestant {
/// #[pk]
/// name: String,
/// rank: i32,
/// nationality: String
/// }
///
/// let conn = butane::db::connect(&ConnectionSpec::new("sqlite", "foo.db")).unwrap();
/// let alice: Result<Contestant, butane::Error> = find!(Contestant, name == "Alice", &conn);
///```
///
/// [`filter]: crate::filter
/// [`Result`]: crate::Result
/// [`NoSuchObject`]: crate::Error::NoSuchObject
#[macro_export]
macro_rules! find {
($dbobj:ident, $filter:expr, $conn:expr) => {
butane::query!($dbobj, $filter)
.limit(1)
.load($conn)
.and_then(|mut results| results.pop().ok_or(butane::Error::NoSuchObject))
};
}
pub mod prelude {
//! Prelude module to improve ergonomics.
//!
//! Its use is recommended, but not required. If not used, the use
//! of butane's macros may require some of its re-exports to be
//! used manually.
#[doc(no_inline)]
pub use crate::DataObject;
#[doc(no_inline)]
pub use crate::DataResult;
pub use butane_core::db::BackendConnection;
}