use super::HasTable;
use crate::dsl::{Eq, EqAny, Filter, FindBy};
use crate::expression::array_comparison::AsInExpression;
use crate::expression::AsExpression;
use crate::prelude::*;
use crate::query_dsl::methods::FilterDsl;
use crate::sql_types::SqlType;
use std::borrow::Borrow;
use std::hash::Hash;
pub trait BelongsTo<Parent> {
type ForeignKey: Hash + ::std::cmp::Eq;
type ForeignKeyColumn: Column;
fn foreign_key(&self) -> Option<&Self::ForeignKey>;
fn foreign_key_column() -> Self::ForeignKeyColumn;
}
pub trait GroupedBy<'a, Parent>: IntoIterator + Sized {
fn grouped_by(self, parents: &'a [Parent]) -> Vec<Vec<Self::Item>>;
fn try_grouped_by(
self,
parents: &'a [Parent],
) -> Result<Vec<Vec<Self::Item>>, TryGroupedByError<Self::Item>> {
Ok(self.grouped_by(parents))
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[non_exhaustive]
pub struct TryGroupedByError<Child> {
pub grouped: Vec<Vec<Child>>,
pub ungrouped: Vec<Child>,
}
type Id<T> = <T as Identifiable>::Id;
impl<Child> TryGroupedByError<Child> {
pub fn new(grouped: Vec<Vec<Child>>, ungrouped: Vec<Child>) -> Self {
Self { grouped, ungrouped }
}
}
impl<'a, Parent: 'a, Child, Iter> GroupedBy<'a, Parent> for Iter
where
Iter: IntoIterator<Item = Child>,
Child: BelongsTo<Parent>,
&'a Parent: Identifiable,
Id<&'a Parent>: Borrow<Child::ForeignKey>,
{
fn grouped_by(self, parents: &'a [Parent]) -> Vec<Vec<Child>> {
use std::collections::HashMap;
use std::iter;
let mut grouped: Vec<_> = iter::repeat_with(Vec::new).take(parents.len()).collect();
let id_indices: HashMap<_, _> = parents
.iter()
.enumerate()
.map(|(i, u)| (u.id(), i))
.collect();
self.into_iter()
.filter_map(|child| {
let fk = child.foreign_key()?;
let i = id_indices.get(fk)?;
Some((i, child))
})
.for_each(|(i, child)| grouped[*i].push(child));
grouped
}
fn try_grouped_by(
self,
parents: &'a [Parent],
) -> Result<Vec<Vec<Child>>, TryGroupedByError<Child>> {
use std::collections::HashMap;
use std::iter;
let mut grouped: Vec<_> = iter::repeat_with(Vec::new).take(parents.len()).collect();
let mut ungrouped: Vec<_> = Vec::new();
let id_indices: HashMap<_, _> = parents
.iter()
.enumerate()
.map(|(i, u)| (u.id(), i))
.collect();
for child in self {
child
.foreign_key()
.and_then(|i| id_indices.get(i))
.map_or(&mut ungrouped, |i| &mut grouped[*i])
.push(child);
}
if ungrouped.is_empty() {
Ok(grouped)
} else {
Err(TryGroupedByError::new(grouped, ungrouped))
}
}
}
impl<'a, Parent, Child> BelongingToDsl<&'a Parent> for Child
where
&'a Parent: Identifiable,
Child: HasTable + BelongsTo<Parent>,
Id<&'a Parent>: AsExpression<<Child::ForeignKeyColumn as Expression>::SqlType>,
Child::Table: FilterDsl<Eq<Child::ForeignKeyColumn, Id<&'a Parent>>>,
Child::ForeignKeyColumn: ExpressionMethods,
<Child::ForeignKeyColumn as Expression>::SqlType: SqlType,
{
type Output = FindBy<Child::Table, Child::ForeignKeyColumn, Id<&'a Parent>>;
fn belonging_to(parent: &'a Parent) -> Self::Output {
FilterDsl::filter(Child::table(), Child::foreign_key_column().eq(parent.id()))
}
}
impl<'a, Parent, Child> BelongingToDsl<&'a [Parent]> for Child
where
&'a Parent: Identifiable,
Child: HasTable + BelongsTo<Parent>,
Vec<Id<&'a Parent>>: AsInExpression<<Child::ForeignKeyColumn as Expression>::SqlType>,
<Child as HasTable>::Table: FilterDsl<EqAny<Child::ForeignKeyColumn, Vec<Id<&'a Parent>>>>,
Child::ForeignKeyColumn: ExpressionMethods,
<Child::ForeignKeyColumn as Expression>::SqlType: SqlType,
{
type Output = Filter<Child::Table, EqAny<Child::ForeignKeyColumn, Vec<Id<&'a Parent>>>>;
fn belonging_to(parents: &'a [Parent]) -> Self::Output {
let ids = parents.iter().map(Identifiable::id).collect::<Vec<_>>();
FilterDsl::filter(Child::table(), Child::foreign_key_column().eq_any(ids))
}
}
impl<'a, Parent, Child> BelongingToDsl<(&'a [Parent], &'a [Parent])> for Child
where
&'a Parent: Identifiable,
Child: HasTable + BelongsTo<Parent>,
Vec<Id<&'a Parent>>: AsInExpression<<Child::ForeignKeyColumn as Expression>::SqlType>,
<Child as HasTable>::Table: FilterDsl<EqAny<Child::ForeignKeyColumn, Vec<Id<&'a Parent>>>>,
Child::ForeignKeyColumn: ExpressionMethods,
<Child::ForeignKeyColumn as Expression>::SqlType: SqlType,
{
type Output = Filter<Child::Table, EqAny<Child::ForeignKeyColumn, Vec<Id<&'a Parent>>>>;
fn belonging_to(parents: (&'a [Parent], &'a [Parent])) -> Self::Output {
let ids = parents
.0
.iter()
.chain(parents.1.iter())
.map(Identifiable::id)
.collect::<Vec<_>>();
FilterDsl::filter(Child::table(), Child::foreign_key_column().eq_any(ids))
}
}
impl<'a, Parent, Child> BelongingToDsl<&'a Vec<Parent>> for Child
where
Child: BelongingToDsl<&'a [Parent]>,
{
type Output = Child::Output;
fn belonging_to(parents: &'a Vec<Parent>) -> Self::Output {
Self::belonging_to(&**parents)
}
}