use crate::error::OrmResult;
use crate::query::QueryBuilder;
use async_trait::async_trait;
use sqlx::{PgPool, Row};
#[async_trait]
pub trait Model: Sized + Send + Sync + Unpin + 'static {
fn table_name() -> &'static str;
fn primary_key() -> &'static str {
"id"
}
fn columns() -> &'static [&'static str];
fn query() -> QueryBuilder<Self> {
QueryBuilder::new(Self::table_name(), Self::primary_key())
}
async fn find(id: i64, pool: &PgPool) -> OrmResult<Option<Self>>
where
Self: for<'r> sqlx::FromRow<'r, sqlx::postgres::PgRow>,
{
let sql = format!(
"SELECT * FROM \"{}\" WHERE \"{}\" = $1",
Self::table_name(),
Self::primary_key(),
);
let row = sqlx::query_as::<_, Self>(&sql)
.bind(id)
.fetch_optional(pool)
.await
.map_err(crate::error::OrmError::from_sqlx)?;
Ok(row)
}
async fn find_or_fail(id: i64, pool: &PgPool) -> OrmResult<Self>
where
Self: for<'r> sqlx::FromRow<'r, sqlx::postgres::PgRow>,
{
Self::find(id, pool)
.await?
.ok_or(crate::error::OrmError::NotFound)
}
async fn find_many(ids: &[i64], pool: &PgPool) -> OrmResult<Vec<Self>>
where
Self: for<'r> sqlx::FromRow<'r, sqlx::postgres::PgRow>,
{
if ids.is_empty() {
return Ok(vec![]);
}
let placeholders: Vec<String> = (1..=ids.len()).map(|i| format!("${}", i)).collect();
let sql = format!(
"SELECT * FROM \"{}\" WHERE \"{}\" IN ({})",
Self::table_name(),
Self::primary_key(),
placeholders.join(", "),
);
let mut q = sqlx::query_as::<_, Self>(&sql);
for id in ids {
q = q.bind(id);
}
let rows = q
.fetch_all(pool)
.await
.map_err(crate::error::OrmError::from_sqlx)?;
Ok(rows)
}
async fn all(pool: &PgPool) -> OrmResult<Vec<Self>>
where
Self: for<'r> sqlx::FromRow<'r, sqlx::postgres::PgRow>,
{
let sql = format!("SELECT * FROM \"{}\"", Self::table_name());
let rows = sqlx::query_as::<_, Self>(&sql)
.fetch_all(pool)
.await
.map_err(crate::error::OrmError::from_sqlx)?;
Ok(rows)
}
async fn first(pool: &PgPool) -> OrmResult<Option<Self>>
where
Self: for<'r> sqlx::FromRow<'r, sqlx::postgres::PgRow>,
{
let sql = format!(
"SELECT * FROM \"{}\" ORDER BY \"{}\" ASC LIMIT 1",
Self::table_name(),
Self::primary_key(),
);
let row = sqlx::query_as::<_, Self>(&sql)
.fetch_optional(pool)
.await
.map_err(crate::error::OrmError::from_sqlx)?;
Ok(row)
}
async fn last(pool: &PgPool) -> OrmResult<Option<Self>>
where
Self: for<'r> sqlx::FromRow<'r, sqlx::postgres::PgRow>,
{
let sql = format!(
"SELECT * FROM \"{}\" ORDER BY \"{}\" DESC LIMIT 1",
Self::table_name(),
Self::primary_key(),
);
let row = sqlx::query_as::<_, Self>(&sql)
.fetch_optional(pool)
.await
.map_err(crate::error::OrmError::from_sqlx)?;
Ok(row)
}
async fn count(pool: &PgPool) -> OrmResult<i64> {
let sql = format!("SELECT COUNT(*) FROM \"{}\"", Self::table_name());
let row: (i64,) = sqlx::query_as(&sql)
.fetch_one(pool)
.await
.map_err(crate::error::OrmError::from_sqlx)?;
Ok(row.0)
}
async fn any(pool: &PgPool) -> OrmResult<bool> {
Ok(Self::count(pool).await? > 0)
}
async fn exists(id: i64, pool: &PgPool) -> OrmResult<bool> {
let sql = format!(
"SELECT EXISTS(SELECT 1 FROM \"{}\" WHERE \"{}\" = $1)",
Self::table_name(),
Self::primary_key(),
);
let row: (bool,) = sqlx::query_as(&sql)
.bind(id)
.fetch_one(pool)
.await
.map_err(crate::error::OrmError::from_sqlx)?;
Ok(row.0)
}
}