use crate::errors::OrionError;
use crate::storage::{DbPool, DbTransaction};
pub fn optional_string_value(opt: Option<&str>) -> sea_query::Value {
opt.map(|s| s.to_string().into())
.unwrap_or(sea_query::Value::String(None))
}
pub async fn fetch_required<T>(
pool: &DbPool,
sql: &str,
values: sea_query_binder::SqlxValues,
err: impl FnOnce() -> OrionError,
) -> Result<T, OrionError>
where
T: for<'r> sqlx::FromRow<'r, sqlx::sqlite::SqliteRow> + Send + Unpin,
T: for<'r> sqlx::FromRow<'r, sqlx::postgres::PgRow>,
T: for<'r> sqlx::FromRow<'r, sqlx::mysql::MySqlRow>,
{
pool.fetch_optional_as::<T>(sql, values)
.await?
.ok_or_else(err)
}
pub async fn count_where<I>(
pool: &DbPool,
table: I,
cond: sea_query::Condition,
) -> Result<i64, OrionError>
where
I: sea_query::IntoTableRef,
{
use sea_query::{Asterisk, Expr, Func, Query};
let (sql, values) = crate::storage::build_sqlx(
Query::select()
.expr(Func::count(Expr::col(Asterisk)))
.from(table)
.cond_where(cond),
);
let (total,): (i64,) = pool.fetch_one_as::<(i64,)>(&sql, values).await?;
Ok(total)
}
pub async fn ensure_absent<T>(
pool: &DbPool,
sql: &str,
values: sea_query_binder::SqlxValues,
err: impl FnOnce() -> OrionError,
) -> Result<(), OrionError>
where
T: for<'r> sqlx::FromRow<'r, sqlx::sqlite::SqliteRow> + Send + Unpin,
T: for<'r> sqlx::FromRow<'r, sqlx::postgres::PgRow>,
T: for<'r> sqlx::FromRow<'r, sqlx::mysql::MySqlRow>,
{
if pool.fetch_optional_as::<T>(sql, values).await?.is_some() {
return Err(err());
}
Ok(())
}
pub async fn fetch_required_tx<T>(
tx: &mut DbTransaction,
sql: &str,
values: sea_query_binder::SqlxValues,
err: impl FnOnce() -> OrionError,
) -> Result<T, OrionError>
where
T: for<'r> sqlx::FromRow<'r, sqlx::sqlite::SqliteRow> + Send + Unpin,
T: for<'r> sqlx::FromRow<'r, sqlx::postgres::PgRow>,
T: for<'r> sqlx::FromRow<'r, sqlx::mysql::MySqlRow>,
{
tx.fetch_optional_as::<T>(sql, values)
.await?
.ok_or_else(err)
}
pub fn clamp_pagination(limit: Option<i64>, offset: Option<i64>) -> (i64, i64) {
let limit = limit.unwrap_or(50).clamp(1, 1000);
let offset = offset.unwrap_or(0).max(0);
(limit, offset)
}
pub fn parse_sort_order(sort_order: Option<&str>) -> sea_query::Order {
match sort_order {
Some("asc") => sea_query::Order::Asc,
_ => sea_query::Order::Desc,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn optional_string_some() {
let v = optional_string_value(Some("hello"));
assert_eq!(v, sea_query::Value::String(Some(Box::new("hello".into()))));
}
#[test]
fn optional_string_none() {
let v = optional_string_value(None);
assert_eq!(v, sea_query::Value::String(None));
}
#[test]
fn pagination_defaults() {
assert_eq!(clamp_pagination(None, None), (50, 0));
}
#[test]
fn pagination_clamps() {
assert_eq!(clamp_pagination(Some(0), Some(-5)), (1, 0));
assert_eq!(clamp_pagination(Some(9999), Some(10)), (1000, 10));
}
#[test]
fn sort_order_asc() {
assert!(matches!(
parse_sort_order(Some("asc")),
sea_query::Order::Asc
));
}
#[test]
fn sort_order_desc() {
assert!(matches!(
parse_sort_order(Some("desc")),
sea_query::Order::Desc
));
}
#[test]
fn sort_order_none_defaults_desc() {
assert!(matches!(parse_sort_order(None), sea_query::Order::Desc));
}
}