use std::marker::PhantomData;
use sqlx::FromRow;
use crate::query::QueryBuilder;
use crate::Error;
pub struct ModelRegistration {
pub class: &'static str,
pub table: &'static str,
pub columns: &'static [&'static str],
}
inventory::collect!(ModelRegistration);
pub fn registered_models() -> Vec<&'static ModelRegistration> {
inventory::iter::<ModelRegistration>.into_iter().collect()
}
pub trait Model: Sized + Send + Sync + Unpin + 'static
where
for<'r> Self: FromRow<'r, sqlx::postgres::PgRow>,
{
const SOFT_DELETES: bool = false;
type PrimaryKey: sqlx::Type<sqlx::Postgres>
+ for<'q> sqlx::Encode<'q, sqlx::Postgres>
+ Send
+ Sync
+ Clone
+ 'static;
const TABLE: &'static str;
const PK_COLUMN: &'static str = "id";
const COLUMNS: &'static [&'static str];
fn primary_key(&self) -> &Self::PrimaryKey;
fn query() -> QueryBuilder<Self> {
QueryBuilder::new()
}
fn find(
pool: &sqlx::PgPool,
id: Self::PrimaryKey,
) -> futures::future::BoxFuture<'_, Result<Option<Self>, Error>> {
Box::pin(async move {
let sql = format!(
"SELECT {} FROM {} WHERE {} = $1 LIMIT 1",
Self::COLUMNS.join(", "),
Self::TABLE,
Self::PK_COLUMN,
);
let result = sqlx::query_as::<_, Self>(&sql)
.bind(id)
.fetch_optional(pool)
.await?;
Ok(result)
})
}
fn all(pool: &sqlx::PgPool) -> futures::future::BoxFuture<'_, Result<Vec<Self>, Error>> {
Box::pin(async move {
let sql = format!("SELECT {} FROM {}", Self::COLUMNS.join(", "), Self::TABLE);
let rows = sqlx::query_as::<_, Self>(&sql).fetch_all(pool).await?;
Ok(rows)
})
}
}
pub struct Loaded<M: Model, R = ()> {
pub items: Vec<M>,
pub _relations: PhantomData<R>,
}
impl<M: Model, R> Loaded<M, R> {
pub fn new(items: Vec<M>) -> Self {
Self {
items,
_relations: PhantomData,
}
}
pub fn into_inner(self) -> Vec<M> {
self.items
}
pub fn iter(&self) -> std::slice::Iter<'_, M> {
self.items.iter()
}
}