use sqlx::{Pool, Postgres, Row};
use crate::error::{ModelError, ModelResult};
use crate::model::core_trait::Model;
use crate::query::QueryBuilder;
#[allow(async_fn_in_trait)]
pub trait QueryMethods: Model {
fn query() -> QueryBuilder<Self>
where
Self: Sized,
{
let builder = QueryBuilder::new().from(Self::table_name());
if Self::uses_soft_deletes() {
builder.where_null("deleted_at")
} else {
builder
}
}
async fn all(pool: &Pool<Postgres>) -> ModelResult<Vec<Self>>
where
Self: Sized,
{
let sql = if Self::uses_soft_deletes() {
format!(
"SELECT * FROM {} WHERE deleted_at IS NULL",
Self::table_name()
)
} else {
format!("SELECT * FROM {}", Self::table_name())
};
let rows = sqlx::query(&sql).fetch_all(pool).await.map_err(|e| {
ModelError::Database(format!(
"Failed to fetch all from {}: {}",
Self::table_name(),
e
))
})?;
let mut models = Vec::new();
for row in rows {
models.push(Self::from_row(&row)?);
}
Ok(models)
}
async fn count(pool: &Pool<Postgres>) -> ModelResult<i64>
where
Self: Sized,
{
let sql = if Self::uses_soft_deletes() {
format!(
"SELECT COUNT(*) FROM {} WHERE deleted_at IS NULL",
Self::table_name()
)
} else {
format!("SELECT COUNT(*) FROM {}", Self::table_name())
};
let row = sqlx::query(&sql).fetch_one(pool).await.map_err(|e| {
ModelError::Database(format!("Failed to count {}: {}", Self::table_name(), e))
})?;
let count: i64 = row
.try_get(0)
.map_err(|e| ModelError::Database(format!("Failed to extract count: {}", e)))?;
Ok(count)
}
async fn where_field<V>(pool: &Pool<Postgres>, field: &str, value: V) -> ModelResult<Vec<Self>>
where
Self: Sized,
V: Send + Sync + 'static,
for<'q> V: sqlx::Encode<'q, Postgres> + sqlx::Type<Postgres>,
{
let sql = if Self::uses_soft_deletes() {
format!(
"SELECT * FROM {} WHERE {} = $1 AND deleted_at IS NULL",
Self::table_name(),
field
)
} else {
format!("SELECT * FROM {} WHERE {} = $1", Self::table_name(), field)
};
let rows = sqlx::query(&sql)
.bind(value)
.fetch_all(pool)
.await
.map_err(|e| {
ModelError::Database(format!(
"Failed to query {} by {}: {}",
Self::table_name(),
field,
e
))
})?;
let mut models = Vec::new();
for row in rows {
models.push(Self::from_row(&row)?);
}
Ok(models)
}
async fn first_where<V>(
pool: &Pool<Postgres>,
field: &str,
value: V,
) -> ModelResult<Option<Self>>
where
Self: Sized,
V: Send + Sync + 'static,
for<'q> V: sqlx::Encode<'q, Postgres> + sqlx::Type<Postgres>,
{
let sql = if Self::uses_soft_deletes() {
format!(
"SELECT * FROM {} WHERE {} = $1 AND deleted_at IS NULL LIMIT 1",
Self::table_name(),
field
)
} else {
format!(
"SELECT * FROM {} WHERE {} = $1 LIMIT 1",
Self::table_name(),
field
)
};
let row = sqlx::query(&sql)
.bind(value)
.fetch_optional(pool)
.await
.map_err(|e| {
ModelError::Database(format!(
"Failed to find first {} by {}: {}",
Self::table_name(),
field,
e
))
})?;
match row {
Some(row) => {
let model = Self::from_row(&row)?;
Ok(Some(model))
}
None => Ok(None),
}
}
async fn first(pool: &Pool<Postgres>) -> ModelResult<Option<Self>>
where
Self: Sized,
{
let sql = if Self::uses_soft_deletes() {
format!(
"SELECT * FROM {} WHERE deleted_at IS NULL LIMIT 1",
Self::table_name()
)
} else {
format!("SELECT * FROM {} LIMIT 1", Self::table_name())
};
let row = sqlx::query(&sql).fetch_optional(pool).await.map_err(|e| {
ModelError::Database(format!("Failed to get first {}: {}", Self::table_name(), e))
})?;
match row {
Some(row) => {
let model = Self::from_row(&row)?;
Ok(Some(model))
}
None => Ok(None),
}
}
async fn last(pool: &Pool<Postgres>) -> ModelResult<Option<Self>>
where
Self: Sized,
{
let sql = if Self::uses_soft_deletes() {
format!(
"SELECT * FROM {} WHERE deleted_at IS NULL ORDER BY {} DESC LIMIT 1",
Self::table_name(),
Self::primary_key_name()
)
} else {
format!(
"SELECT * FROM {} ORDER BY {} DESC LIMIT 1",
Self::table_name(),
Self::primary_key_name()
)
};
let row = sqlx::query(&sql).fetch_optional(pool).await.map_err(|e| {
ModelError::Database(format!("Failed to get last {}: {}", Self::table_name(), e))
})?;
match row {
Some(row) => {
let model = Self::from_row(&row)?;
Ok(Some(model))
}
None => Ok(None),
}
}
}
impl<T: Model> QueryMethods for T {}