use fraiseql_db::ViewName;
use super::resolve_cascade_views;
use crate::{
runtime::mutation_result::MutationOutcome,
schema::{CompiledSchema, MutationDefinition},
};
#[derive(Debug, Default, PartialEq, Eq)]
pub(super) struct InvalidationPlan {
pub views: Vec<ViewName>,
pub entity: Option<(String, String)>,
}
impl InvalidationPlan {
pub(super) fn with_reported_types(
mut self,
types: &std::collections::BTreeSet<String>,
schema: &CompiledSchema,
) -> Self {
for view in types.iter().filter_map(|t| view_of_type(schema, t)) {
push_view(&mut self.views, view);
}
self
}
}
impl InvalidationPlan {
pub(super) fn absorb(&mut self, other: Self) -> Option<(String, String)> {
for view in other.views {
push_view(&mut self.views, view);
}
other.entity
}
}
fn push_view(views: &mut Vec<ViewName>, view: ViewName) {
if !views.iter().any(|v| v.as_str() == view.as_str()) {
views.push(view);
}
}
use crate::cache::mutation_reach::{statically_resolved_views, view_of_type};
pub(super) fn plan_invalidation(
mutation_def: &MutationDefinition,
outcome: &MutationOutcome,
schema: &CompiledSchema,
) -> InvalidationPlan {
let MutationOutcome::Success {
entity_type,
entity_id,
cascade,
..
} = outcome
else {
return InvalidationPlan::default();
};
let mut views: Vec<ViewName> = statically_resolved_views(mutation_def, schema);
if let Some(etype) = entity_type.as_deref() {
if let Some(view) = view_of_type(schema, etype) {
push_view(&mut views, view);
}
}
if mutation_def.cascade {
if let Some(cascade_json) = cascade.as_ref() {
for view in resolve_cascade_views(cascade_json, schema) {
push_view(&mut views, view);
}
}
}
let entity = match (entity_type.as_deref(), entity_id.as_deref()) {
(Some(etype), Some(eid)) => Some((etype.to_string(), eid.to_string())),
_ => None,
};
InvalidationPlan { views, entity }
}
#[cfg(test)]
#[path = "invalidation_tests.rs"]
mod invalidation_tests;