use std::collections::BTreeSet;
use super::stage::Stage;
use crate::{pcp, sdf, tf};
pub type StageSinkId = sdf::sink::Id<dyn StageSink>;
pub trait StageSink {
fn after_commit(&self, stage: &Stage, change: &CommittedChange<'_>) {
let _ = (stage, change);
}
fn before_commit(&self, stage: &Stage, change: &PendingChange<'_>) {
let _ = (stage, change);
}
fn edit_target_changed(&self, stage: &Stage) {
let _ = stage;
}
fn layer_muting_changed(&self, stage: &Stage, layer: &str, muted: bool) {
let _ = (stage, layer, muted);
}
fn load_rules_changed(&self, stage: &Stage, resynced: &[sdf::Path]) {
let _ = (stage, resynced);
}
}
#[derive(Debug, Clone)]
pub enum Provenance {
LocalStack,
EditTarget(pcp::MapFunction),
DirectLayerEdit,
}
impl Provenance {
pub fn mapping(&self) -> Option<&pcp::MapFunction> {
match self {
Provenance::EditTarget(m) => Some(m),
Provenance::LocalStack | Provenance::DirectLayerEdit => None,
}
}
}
pub struct CommittedChange<'a> {
pub resynced: &'a [sdf::Path],
pub changed_info_only: &'a [sdf::Path],
pub layer_identifier: &'a str,
pub change_list: &'a sdf::ChangeList,
pub layer_changes: &'a [(String, sdf::ChangeList)],
pub provenance: &'a Provenance,
pub generation: u64,
}
impl CommittedChange<'_> {
pub fn changed_fields(&self, path: &sdf::Path) -> &BTreeSet<tf::Token> {
static EMPTY: BTreeSet<tf::Token> = BTreeSet::new();
let key = match self.provenance.mapping() {
Some(m) => match m.map_target_to_source(path) {
Some(key) => key,
None => return &EMPTY,
},
None => path.clone(),
};
self.change_list
.entries()
.iter()
.find(|(p, _)| p == &key)
.map(|(_, entry)| &entry.info_changed)
.unwrap_or(&EMPTY)
}
}
pub struct PendingChange<'a> {
pub layer_identifier: &'a str,
pub base: &'a dyn sdf::AbstractData,
pub change_list: &'a sdf::ChangeList,
pub mapping: Option<&'a pcp::MapFunction>,
pub generation: u64,
}
impl<F: Fn(&Stage, &CommittedChange<'_>)> StageSink for F {
fn after_commit(&self, stage: &Stage, change: &CommittedChange<'_>) {
self(stage, change);
}
}
pub(super) struct Payload {
resynced: Vec<sdf::Path>,
changed_info_only: Vec<sdf::Path>,
change_list: sdf::ChangeList,
layer_changes: Vec<(String, sdf::ChangeList)>,
}
impl Payload {
pub(super) fn new(
changes: &pcp::Changes,
scratch: &sdf::ChangeList,
layer_changes: Vec<(String, sdf::ChangeList)>,
provenance: &Provenance,
) -> Self {
let mapping = provenance.mapping();
let mut resynced: Vec<sdf::Path> = changes
.cache
.resynced_paths()
.map(|p| match mapping {
Some(m) => m.map_source_to_target(p).unwrap_or_else(|| p.clone()),
None => p.clone(),
})
.collect();
resynced.sort();
resynced.dedup();
let mut changed_info_only: Vec<sdf::Path> = scratch
.entries()
.iter()
.filter(|(_, e)| {
let removed = e.flags.contains(sdf::ChangeFlags::REMOVE_PROPERTY)
&& !e.flags.contains(sdf::ChangeFlags::ADD_PROPERTY);
!removed
&& (!e.info_changed.is_empty()
|| e.flags.intersects(
sdf::ChangeFlags::CHANGE_RELATIONSHIP_TARGETS
| sdf::ChangeFlags::CHANGE_ATTRIBUTE_CONNECTION,
))
})
.filter_map(|(p, _)| match mapping {
Some(m) => m.map_source_to_target(p),
None => Some(p.clone()),
})
.filter(|p| resynced.binary_search(p).is_err())
.collect();
changed_info_only.sort();
changed_info_only.dedup();
Self {
resynced,
changed_info_only,
change_list: scratch.clone(),
layer_changes,
}
}
pub(super) fn record_root_resync(&mut self) {
let root = sdf::Path::abs_root();
if let Err(at) = self.resynced.binary_search(&root) {
self.resynced.insert(at, root.clone());
}
self.changed_info_only.retain(|p| *p != root);
}
pub(super) fn committed_change<'a>(
&'a self,
layer_identifier: &'a str,
provenance: &'a Provenance,
generation: u64,
) -> CommittedChange<'a> {
CommittedChange {
resynced: &self.resynced,
changed_info_only: &self.changed_info_only,
layer_identifier,
change_list: &self.change_list,
layer_changes: &self.layer_changes,
provenance,
generation,
}
}
}