#![allow(clippy::iter_nth_zero)]
#![allow(clippy::upper_case_acronyms)] use serde::{Deserialize, Serialize};
use std::borrow::Borrow;
use std::cmp::{Eq, PartialEq};
use std::default::Default;
use thiserror::Error as ThisError;
pub mod codegen;
pub mod custom;
pub mod db;
pub mod fkey;
pub mod many;
pub mod migrations;
pub mod query;
pub mod sqlval;
#[cfg(feature = "uuid")]
pub mod uuid;
#[cfg(feature = "fake")]
use fake::{Dummy, Faker};
use db::{BackendRow, Column, ConnectionMethods};
use custom::SqlTypeCustom;
pub use query::Query;
pub use sqlval::*;
pub type Result<T> = std::result::Result<T, crate::Error>;
#[derive(Clone, Debug, Default)]
pub struct ObjectState {
pub saved: bool,
}
impl PartialEq<ObjectState> for ObjectState {
fn eq(&self, _other: &ObjectState) -> bool {
true
}
}
impl Eq for ObjectState {}
#[cfg(feature = "fake")]
impl Dummy<Faker> for ObjectState {
fn dummy_with_rng<R: rand::Rng + ?Sized>(_: &Faker, _rng: &mut R) -> Self {
Self::default()
}
}
pub trait DataResult: Sized {
type DBO: DataObject;
const COLUMNS: &'static [Column];
fn from_row<'a>(row: &(dyn BackendRow + 'a)) -> Result<Self>
where
Self: Sized;
fn query() -> Query<Self>;
}
pub trait DataObject: DataResult<DBO = Self> {
type PKType: PrimaryKeyType;
type Fields: Default;
const PKCOL: &'static str;
const TABLE: &'static str;
const AUTO_PK: bool;
fn pk(&self) -> &Self::PKType;
fn get(conn: &impl ConnectionMethods, id: impl Borrow<Self::PKType>) -> Result<Self>
where
Self: Sized,
{
Self::try_get(conn, id)?.ok_or(Error::NoSuchObject)
}
fn try_get(conn: &impl ConnectionMethods, id: impl Borrow<Self::PKType>) -> Result<Option<Self>>
where
Self: Sized,
{
Ok(<Self as DataResult>::query()
.filter(query::BoolExpr::Eq(
Self::PKCOL,
query::Expr::Val(id.borrow().to_sql()),
))
.limit(1)
.load(conn)?
.into_iter()
.nth(0))
}
fn save(&mut self, conn: &impl ConnectionMethods) -> Result<()>;
fn delete(&self, conn: &impl ConnectionMethods) -> Result<()>;
}
pub trait ModelTyped {
type Model: DataObject;
}
#[derive(Debug, ThisError)]
pub enum Error {
#[error("No such object exists")]
NoSuchObject,
#[error("Index out of bounds {0}")]
BoundsError(String),
#[error("Type mismatch converting SqlVal. Expected {0}, found value {1:?}")]
CannotConvertSqlVal(SqlType, SqlVal),
#[error(
"Mismatch between sql types and rust types while loading data for column {col}. {detail}"
)]
SqlResultTypeMismatch { col: String, detail: String },
#[error("SqlType not known for {0}")]
UnknownSqlType(String),
#[error("Value has not been loaded from the database")]
ValueNotLoaded,
#[error("Cannot use value not saved to the database")]
ValueNotSaved,
#[error("Not initialized")]
NotInitialized,
#[error("Already initialized")]
AlreadyInitialized,
#[error("Migration error {0}")]
MigrationError(String),
#[error("Unknown backend {0}")]
UnknownBackend(String),
#[error("Range error")]
OutOfRange,
#[error("Internal logic error {0}")]
Internal(String),
#[error("Cannot resolve type {0}. Are you missing a #[butane_type] attribute?")]
CannotResolveType(String),
#[error("Auto fields are only supported for integer fields. {0} cannot be auto.")]
InvalidAuto(String),
#[error("No implicit default available for custom sql types.")]
NoCustomDefault,
#[error("No enum variant named '{0}'")]
UnknownEnumVariant(String),
#[error("Backend {1} is not compatible with custom SqlVal {0:?}")]
IncompatibleCustom(custom::SqlValCustom, &'static str),
#[error("Backend {1} is not compatible with custom SqlType {0:?}")]
IncompatibleCustomT(custom::SqlTypeCustom, &'static str),
#[error("Literal values for custom types are currently unsupported.")]
LiteralForCustomUnsupported(custom::SqlValCustom),
#[error("(De)serialization error {0}")]
SerdeJson(#[from] serde_json::Error),
#[error("IO error {0}")]
IO(#[from] std::io::Error),
#[cfg(feature = "sqlite")]
#[error("Sqlite error {0}")]
SQLite(#[from] rusqlite::Error),
#[cfg(feature = "sqlite")]
#[error("Sqlite error {0}")]
SQLiteFromSQL(rusqlite::types::FromSqlError),
#[cfg(feature = "pg")]
#[error("Postgres error {0}")]
Postgres(#[from] postgres::Error),
#[cfg(feature = "datetime")]
#[error("Chrono error {0}")]
Chrono(#[from] chrono::ParseError),
#[error("RefCell error {0}")]
CellBorrow(#[from] std::cell::BorrowMutError),
#[cfg(feature = "tls")]
#[error("TLS error {0}")]
TLS(#[from] native_tls::Error),
#[error("Generic error {0}")]
Generic(#[from] Box<dyn std::error::Error + Sync + Send>),
}
#[cfg(feature = "sqlite")]
impl From<rusqlite::types::FromSqlError> for Error {
fn from(e: rusqlite::types::FromSqlError) -> Self {
use rusqlite::types::FromSqlError;
match &e {
FromSqlError::InvalidType => Error::SqlResultTypeMismatch {
col: "unknown".to_string(),
detail: "unknown".to_string(),
},
FromSqlError::OutOfRange(_) => Error::OutOfRange,
FromSqlError::Other(_) => Error::SQLiteFromSQL(e),
_ => Error::SQLiteFromSQL(e),
}
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub enum SqlType {
Bool,
Int,
BigInt,
Real,
Text,
#[cfg(feature = "datetime")]
Timestamp,
Blob,
#[cfg(feature = "json")]
Json,
Custom(SqlTypeCustom),
}
impl std::fmt::Display for SqlType {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
use SqlType::*;
match &self {
Bool => "bool",
Int => "int",
BigInt => "big int",
Real => "float",
Text => "string",
#[cfg(feature = "datetime")]
Timestamp => "timestamp",
Blob => "blob",
#[cfg(feature = "json")]
Json => "json",
Custom(_) => "custom",
}
.fmt(f)
}
}
#[cfg(feature = "log")]
pub use log::debug;
#[cfg(feature = "log")]
pub use log::warn;
#[cfg(not(feature = "log"))]
mod btlog {
#[macro_export]
macro_rules! debug {
(target: $target:expr, $($arg:tt)+) => {};
($($arg:tt)+) => {};
}
#[macro_export]
macro_rules! warn {
(target: $target:expr, $($arg:tt)+) => {};
($($arg:tt)+) => {};
}
}