use std::future::Future;
use std::pin::Pin;
use crate::error::QueryResult;
use crate::filter::Filter;
pub trait Model: Sized + Send + Sync {
const MODEL_NAME: &'static str;
const TABLE_NAME: &'static str;
const PRIMARY_KEY: &'static [&'static str];
const COLUMNS: &'static [&'static str];
}
pub trait View: Sized + Send + Sync {
const VIEW_NAME: &'static str;
const DB_VIEW_NAME: &'static str;
const COLUMNS: &'static [&'static str];
const IS_MATERIALIZED: bool;
}
pub trait MaterializedView: View {
const SUPPORTS_CONCURRENT_REFRESH: bool = true;
}
pub trait IntoFilter {
fn into_filter(self) -> Filter;
}
impl IntoFilter for Filter {
fn into_filter(self) -> Filter {
self
}
}
impl<F: FnOnce() -> Filter> IntoFilter for F {
fn into_filter(self) -> Filter {
self()
}
}
pub trait Executable {
type Output;
fn exec(self) -> impl Future<Output = QueryResult<Self::Output>> + Send;
}
pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
pub trait QueryEngine: Send + Sync + Clone + 'static {
fn query_many<T: Model + Send + 'static>(
&self,
sql: &str,
params: Vec<crate::filter::FilterValue>,
) -> BoxFuture<'_, QueryResult<Vec<T>>>;
fn query_one<T: Model + Send + 'static>(
&self,
sql: &str,
params: Vec<crate::filter::FilterValue>,
) -> BoxFuture<'_, QueryResult<T>>;
fn query_optional<T: Model + Send + 'static>(
&self,
sql: &str,
params: Vec<crate::filter::FilterValue>,
) -> BoxFuture<'_, QueryResult<Option<T>>>;
fn execute_insert<T: Model + Send + 'static>(
&self,
sql: &str,
params: Vec<crate::filter::FilterValue>,
) -> BoxFuture<'_, QueryResult<T>>;
fn execute_update<T: Model + Send + 'static>(
&self,
sql: &str,
params: Vec<crate::filter::FilterValue>,
) -> BoxFuture<'_, QueryResult<Vec<T>>>;
fn execute_delete(
&self,
sql: &str,
params: Vec<crate::filter::FilterValue>,
) -> BoxFuture<'_, QueryResult<u64>>;
fn execute_raw(
&self,
sql: &str,
params: Vec<crate::filter::FilterValue>,
) -> BoxFuture<'_, QueryResult<u64>>;
fn count(
&self,
sql: &str,
params: Vec<crate::filter::FilterValue>,
) -> BoxFuture<'_, QueryResult<u64>>;
fn refresh_materialized_view(
&self,
view_name: &str,
concurrently: bool,
) -> BoxFuture<'_, QueryResult<()>> {
let view_name = view_name.to_string();
Box::pin(async move {
let _ = (view_name, concurrently);
Err(crate::error::QueryError::unsupported(
"Materialized view refresh is not supported by this database",
))
})
}
}
pub trait ViewQueryEngine: QueryEngine {
fn query_view_many<V: View + Send + 'static>(
&self,
sql: &str,
params: Vec<crate::filter::FilterValue>,
) -> BoxFuture<'_, QueryResult<Vec<V>>>;
fn query_view_optional<V: View + Send + 'static>(
&self,
sql: &str,
params: Vec<crate::filter::FilterValue>,
) -> BoxFuture<'_, QueryResult<Option<V>>>;
fn count_view(
&self,
sql: &str,
params: Vec<crate::filter::FilterValue>,
) -> BoxFuture<'_, QueryResult<u64>> {
self.count(sql, params)
}
}
pub trait ModelAccessor<E: QueryEngine>: Send + Sync {
type Model: Model;
fn engine(&self) -> &E;
fn find_many(&self) -> crate::operations::FindManyOperation<E, Self::Model>;
fn find_unique(&self) -> crate::operations::FindUniqueOperation<E, Self::Model>;
fn find_first(&self) -> crate::operations::FindFirstOperation<E, Self::Model>;
fn create(
&self,
data: <Self::Model as CreateData>::Data,
) -> crate::operations::CreateOperation<E, Self::Model>
where
Self::Model: CreateData;
fn update(&self) -> crate::operations::UpdateOperation<E, Self::Model>;
fn delete(&self) -> crate::operations::DeleteOperation<E, Self::Model>;
fn upsert(
&self,
create: <Self::Model as CreateData>::Data,
update: <Self::Model as UpdateData>::Data,
) -> crate::operations::UpsertOperation<E, Self::Model>
where
Self::Model: CreateData + UpdateData;
fn count(&self) -> crate::operations::CountOperation<E, Self::Model>;
}
pub trait CreateData: Model {
type Data: Send + Sync;
}
pub trait UpdateData: Model {
type Data: Send + Sync;
}
pub trait UpsertData: CreateData + UpdateData {}
impl<T: CreateData + UpdateData> UpsertData for T {}
pub trait WithRelations: Model {
type Include;
type Select;
}
#[cfg(test)]
mod tests {
use super::*;
struct TestModel;
impl Model for TestModel {
const MODEL_NAME: &'static str = "TestModel";
const TABLE_NAME: &'static str = "test_models";
const PRIMARY_KEY: &'static [&'static str] = &["id"];
const COLUMNS: &'static [&'static str] = &["id", "name", "email"];
}
#[test]
fn test_model_trait() {
assert_eq!(TestModel::MODEL_NAME, "TestModel");
assert_eq!(TestModel::TABLE_NAME, "test_models");
assert_eq!(TestModel::PRIMARY_KEY, &["id"]);
}
#[test]
fn test_into_filter() {
let filter = Filter::Equals("id".into(), crate::filter::FilterValue::Int(1));
let converted = filter.clone().into_filter();
assert_eq!(converted, filter);
}
}