use std::any::Any;
use std::collections::HashMap;
use std::marker::PhantomData;
use std::ops::Deref;
pub struct Loaded;
pub struct Unloaded;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RelationKind {
HasMany,
HasOne,
BelongsTo,
}
pub struct HasMany<T> {
fk: &'static str,
_target: PhantomData<T>,
}
impl<T> HasMany<T> {
pub const fn new(foreign_key: &'static str) -> Self {
Self {
fk: foreign_key,
_target: PhantomData,
}
}
pub fn foreign_key(&self) -> &'static str {
self.fk
}
pub fn kind(&self) -> RelationKind {
RelationKind::HasMany
}
}
pub struct HasOne<T> {
fk: &'static str,
_target: PhantomData<T>,
}
impl<T> HasOne<T> {
pub const fn new(foreign_key: &'static str) -> Self {
Self {
fk: foreign_key,
_target: PhantomData,
}
}
pub fn foreign_key(&self) -> &'static str {
self.fk
}
pub fn kind(&self) -> RelationKind {
RelationKind::HasOne
}
}
pub struct BelongsTo<T> {
fk: &'static str,
_target: PhantomData<T>,
}
impl<T> BelongsTo<T> {
pub const fn new(foreign_key: &'static str) -> Self {
Self {
fk: foreign_key,
_target: PhantomData,
}
}
pub fn foreign_key(&self) -> &'static str {
self.fk
}
pub fn kind(&self) -> RelationKind {
RelationKind::BelongsTo
}
}
use crate::core::expr::{Expr, OrderExpr};
use crate::core::types::Value;
#[derive(Debug)]
pub struct RelationSpec {
name: &'static str,
foreign_key: &'static str,
target_table: &'static str,
kind: RelationKind,
filters: Vec<Expr>,
order_bys: Vec<OrderExpr>,
limit: Option<u64>,
}
impl RelationSpec {
pub fn new(
name: &'static str,
foreign_key: &'static str,
target_table: &'static str,
kind: RelationKind,
) -> Self {
Self {
name,
foreign_key,
target_table,
kind,
filters: Vec::new(),
order_bys: Vec::new(),
limit: None,
}
}
pub const fn new_const(
name: &'static str,
foreign_key: &'static str,
target_table: &'static str,
kind: RelationKind,
) -> Self {
Self {
name,
foreign_key,
target_table,
kind,
filters: Vec::new(),
order_bys: Vec::new(),
limit: None,
}
}
pub fn name(&self) -> &'static str {
self.name
}
pub fn foreign_key(&self) -> &'static str {
self.foreign_key
}
pub fn target_table(&self) -> &'static str {
self.target_table
}
pub fn kind(&self) -> RelationKind {
self.kind
}
pub fn limit(&self) -> Option<u64> {
self.limit
}
pub fn has_filters(&self) -> bool {
!self.filters.is_empty()
}
#[allow(non_snake_case)]
pub fn Filter(mut self, expr: Expr) -> Self {
self.filters.push(expr);
self
}
#[allow(non_snake_case)]
pub fn OrderBy(mut self, order: OrderExpr) -> Self {
self.order_bys.push(order);
self
}
#[allow(non_snake_case)]
pub fn Limit(mut self, n: u64) -> Self {
self.limit = Some(n);
self
}
pub fn build_batch_sql(&self, parent_ids: &[Value]) -> (String, Vec<Value>) {
let mut sql = format!(
"SELECT \"{}\".* FROM \"{}\"",
self.target_table, self.target_table
);
let mut binds = Vec::new();
let mut idx = 1usize;
let placeholders: Vec<String> = parent_ids
.iter()
.map(|v| {
binds.push(v.clone());
let p = format!("${idx}");
idx += 1;
p
})
.collect();
sql.push_str(&format!(
" WHERE \"{}\" IN ({})",
self.foreign_key,
placeholders.join(", ")
));
for filter in &self.filters {
sql.push_str(&format!(" AND {}", filter.to_sql(idx)));
binds.extend(filter.binds());
idx += filter.bind_count();
}
if !self.order_bys.is_empty() {
let orders: Vec<String> = self.order_bys.iter().map(|o| o.to_sql_bare()).collect();
sql.push_str(&format!(" ORDER BY {}", orders.join(", ")));
}
if let Some(limit) = self.limit {
sql.push_str(&format!(" LIMIT {limit}"));
}
(sql, binds)
}
}
pub struct RelationStore {
data: HashMap<&'static str, Box<dyn Any + Send + Sync>>,
}
impl RelationStore {
pub fn new() -> Self {
Self {
data: HashMap::new(),
}
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
pub fn insert_decoded<T: Any + Send + Sync>(&mut self, name: &'static str, data: T) {
self.data.insert(name, Box::new(data));
}
pub fn get<T: Any>(&self, name: &str) -> Option<&T> {
self.data.get(name)?.downcast_ref::<T>()
}
}
impl Default for RelationStore {
fn default() -> Self {
Self::new()
}
}
pub struct WithRelations<M, State = ()> {
model: M,
relations: RelationStore,
_state: PhantomData<State>,
}
impl<M, S> WithRelations<M, S> {
pub fn new(model: M, relations: RelationStore) -> Self {
Self {
model,
relations,
_state: PhantomData,
}
}
pub fn into_inner(self) -> M {
self.model
}
pub fn relations(&self) -> &RelationStore {
&self.relations
}
}
impl<M> WithRelations<M, ()> {
pub fn bare(model: M) -> Self {
Self::new(model, RelationStore::new())
}
}
#[diagnostic::on_unimplemented(
message = "relation `{Rel}` was not included in the query",
label = "call .Include() to load this relation before accessing it"
)]
pub trait RelationLoaded<Rel> {
type Output: ?Sized;
fn get_relation(&self) -> &Self::Output;
}
pub trait ModelRelations {
type BareState;
}
pub trait IncludeTransition<M, Current, Rel> {
type Next;
}
pub struct RelationInclude<M, Rel> {
spec: RelationSpec,
_marker: PhantomData<(M, Rel)>,
}
impl<M, Rel> RelationInclude<M, Rel> {
pub fn new(spec: RelationSpec) -> Self {
Self {
spec,
_marker: PhantomData,
}
}
pub fn spec(&self) -> &RelationSpec {
&self.spec
}
pub fn into_spec(self) -> RelationSpec {
self.spec
}
}
pub struct RelationRows {
pub rows: std::sync::Arc<Vec<driver::Row>>,
pub parent_pk: Value,
pub foreign_key: &'static str,
}
impl<M, S> Deref for WithRelations<M, S> {
type Target = M;
fn deref(&self) -> &M {
&self.model
}
}