use toasty_core::{
schema::app,
stmt::{self, ExprContext, IntoExprTarget, VisitMut},
};
pub(super) struct RewriteVia<'a> {
cx: ExprContext<'a>,
}
impl<'a> RewriteVia<'a> {
pub(super) fn new(cx: ExprContext<'a>) -> Self {
Self { cx }
}
pub(super) fn rewrite(&mut self, stmt: &mut stmt::Statement) {
self.visit_mut(stmt);
}
fn schema(&self) -> &'a toasty_core::Schema {
self.cx.schema()
}
fn scope<'scope>(&'scope self, target: impl IntoExprTarget<'scope>) -> RewriteVia<'scope> {
RewriteVia {
cx: self.cx.scope(target),
}
}
pub(super) fn rewrite_via_for_delete(&mut self, stmt: &mut stmt::Delete) {
if let stmt::Source::Model(model) = &mut stmt.from
&& let Some(via) = model.via.take()
{
let mut s = self.scope(&stmt.from);
let filter = s.rewrite_association_as_filter(via);
stmt.filter = stmt::Filter::and(stmt.filter.take(), filter);
}
}
pub(super) fn rewrite_via_for_insert(&mut self, stmt: &mut stmt::Insert) {
if let stmt::InsertTarget::Scope(scope) = &mut stmt.target {
self.rewrite_via_for_query(scope);
}
}
pub(super) fn rewrite_via_for_query(&mut self, stmt: &mut stmt::Query) {
if let stmt::ExprSet::Select(select) = &mut stmt.body
&& let stmt::Source::Model(model) = &mut select.source
&& let Some(via) = model.via.take()
{
let mut s = self.scope(&select.source);
let filter = s.rewrite_association_as_filter(via);
select.filter = stmt::Filter::and(select.filter.take(), filter);
}
}
pub(super) fn rewrite_association_as_filter(
&mut self,
mut association: stmt::Association,
) -> stmt::Filter {
stmt::visit_mut::visit_stmt_query_mut(self, &mut association.source);
assert!(association.path.len() == 1, "TODO");
let Some(field) = self.schema().app.resolve_field_path(&association.path) else {
todo!()
};
match &field.ty {
app::FieldTy::BelongsTo(rel) => {
self.rewrite_association_belongs_to_as_filter(rel, association)
}
app::FieldTy::HasOne(rel) => {
stmt::Expr::in_subquery(stmt::Expr::ref_self_field(rel.pair), *association.source)
.into()
}
app::FieldTy::HasMany(rel) => {
stmt::Expr::in_subquery(stmt::Expr::ref_self_field(rel.pair), *association.source)
.into()
}
_ => todo!("field={field:#?}"),
}
}
fn rewrite_association_belongs_to_as_filter(
&mut self,
rel: &app::BelongsTo,
association: stmt::Association,
) -> stmt::Filter {
todo!("rel={rel:#?}, association={association:#?}");
}
}
impl VisitMut for RewriteVia<'_> {
fn visit_stmt_delete_mut(&mut self, i: &mut stmt::Delete) {
self.rewrite_via_for_delete(i);
stmt::visit_mut::visit_stmt_delete_mut(self, i);
}
fn visit_stmt_insert_mut(&mut self, i: &mut stmt::Insert) {
self.rewrite_via_for_insert(i);
stmt::visit_mut::visit_stmt_insert_mut(self, i);
}
fn visit_stmt_query_mut(&mut self, i: &mut stmt::Query) {
self.rewrite_via_for_query(i);
stmt::visit_mut::visit_stmt_query_mut(self, i);
}
}