use rusqlite::{Row, ToSql, params};
#[cfg(feature = "tracing")]
use tracing::debug;
use crate::{Database, Res, Table, flatten_traced};
pub trait Select<R>
where
Self: Table,
{
const SELECT: &str;
fn select(database: &Database) -> Res<Vec<R>> {
let mut stmt = database.prepare_reader(Self::SELECT)?;
let rows: Vec<R> = stmt.query_map((), Self::func)?.flatten().collect();
#[cfg(feature = "tracing")]
debug!("Rows returned from select: {}", rows.len());
Ok(rows)
}
fn select_limited(database: &Database, limit: usize) -> Res<Vec<R>> {
let limited_query = format!("{} LIMIT {limit}", Self::SELECT);
let mut stmt = database.prepare_reader(limited_query)?;
let rows: Vec<R> = stmt.query_map((), Self::func)?.flatten().collect();
debug_assert!(
rows.len() <= limit,
"Returned '{}' rows, but the maximum amount is '{}'",
rows.len(),
limit
);
Ok(rows)
}
fn select_one(database: &Database) -> Res<Option<R>> {
let mut rows = Self::select_limited(database, 1)?;
if rows.len() == 1 {
Ok(Some(rows.swap_remove(0)))
} else if rows.is_empty() {
Ok(None)
} else {
Err(format!(
"No items returned from select on '{}' table",
Self::tbl_name()
)
.into())
}
}
fn select_distinct(database: &Database) -> Res<Vec<R>> {
let distinct_query = Self::SELECT.replace("SELECT", "SELECT DISTINCT");
let mut stmt = database.prepare_reader(distinct_query)?;
let rows: Vec<R> = stmt.query_map((), Self::func)?.flatten().collect();
#[cfg(feature = "tracing")]
debug!("Rows returned from select: {}", rows.len());
Ok(rows)
}
fn func(row: &Row) -> Result<R, rusqlite::Error>;
}
pub trait SelectBy<R, B: ToSql>
where
Self: Table,
{
const SELECT_BY: &str;
fn select_by(database: &Database, by: B) -> Res<Vec<R>> {
let mut stmt = database.prepare_reader(Self::SELECT_BY)?;
let rows = stmt.query_map(params![by], Self::func)?.flatten().collect();
Ok(rows)
}
fn select_by_one(database: &Database, by: B) -> Res<R> {
let mut stmt = database.prepare_reader(Self::SELECT_BY)?;
let rows = stmt.query_row(params![by], Self::func)?;
Ok(rows)
}
fn func(row: &Row) -> Result<R, rusqlite::Error>;
}
pub trait SelectInto<R, C: From<Vec<R>>>
where
Self: Table,
{
const SELECT_INTO: &str;
fn select_into(&self, database: &Database) -> Res<C> {
let mut stmt = database.prepare_reader(Self::SELECT_INTO)?;
let rows_iter = stmt.query_map(params![], Self::func)?;
let rows: Vec<R> = flatten_traced::<R, _>(rows_iter).collect();
Ok(rows.into())
}
fn func(row: &Row) -> Result<R, rusqlite::Error>;
}