use crate::filter::{FilterExpr, RelationFilter, RelationQuantifier};
use crate::relation_scope::RelatedReadScope;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Orderable;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Unorderable;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RelationHop {
pub parent_table: &'static str,
pub parent_column: &'static str,
pub related_table: &'static str,
pub related_column: &'static str,
pub quantifier: RelationQuantifier,
pub scope: RelatedReadScope,
}
impl RelationHop {
pub const fn new(
parent_table: &'static str,
parent_column: &'static str,
related_table: &'static str,
related_column: &'static str,
quantifier: RelationQuantifier,
scope: RelatedReadScope,
) -> Self {
Self {
parent_table,
parent_column,
related_table,
related_column,
quantifier,
scope,
}
}
pub const fn with_quantifier(self, quantifier: RelationQuantifier) -> Self {
Self { quantifier, ..self }
}
pub fn is_self_relation(&self) -> bool {
self.parent_table == self.related_table
}
}
pub fn wrap_filter(hops: &[RelationHop], inner: FilterExpr) -> FilterExpr {
hops.iter().rev().fold(inner, |acc, hop| {
FilterExpr::Relation(RelationFilter::new(
hop.quantifier,
hop.parent_table,
hop.parent_column,
hop.related_table,
hop.related_column,
acc,
hop.scope,
))
})
}
pub fn order_value_sql(hops: &[RelationHop], column: &str) -> String {
assert!(
!hops.is_empty(),
"order_value_sql requires at least one relation hop",
);
let mut sql = format!("{}.{}", hops[hops.len() - 1].related_table, column,);
for index in (1..hops.len()).rev() {
let hop = &hops[index];
let current_table = hops[index - 1].related_table;
sql = format!(
"(SELECT {} FROM {} WHERE {}.{} = {}.{} LIMIT 1)",
sql,
hop.related_table,
hop.related_table,
hop.related_column,
current_table,
hop.parent_column,
);
}
sql
}
pub fn is_orderable(hops: &[RelationHop]) -> bool {
hops.iter()
.all(|hop| matches!(hop.quantifier, RelationQuantifier::ToOne))
}
#[cfg(test)]
#[path = "relation_path_tests.rs"]
mod tests;