use std::collections::BTreeMap;
use super::world::{Walked, Wanted, World};
use super::{STREAMED, Stream};
use crate::refs;
use crate::render::table::{Kind, Table, Value};
use crate::render::terms::{NOTES, is_term};
use crate::typing::Typing;
impl Stream {
pub(crate) async fn unlike_rebuilt(&self) -> Option<Vec<String>> {
let mine = &self.world.typing;
let typed = mine.paths().filter(|(p, _)| is_term(p)).count();
if typed != self.terms.count() {
return None;
}
let mut graph = self.world.graph.clone();
graph.set(STREAMED, None);
if !self.world.own_notes {
graph.set(NOTES, None);
}
let mut fresh = World::new(&graph, &self.config.render).expect("a world");
let wanted = Wanted {
target: &self.expr,
terms: &self.terms,
term: None,
from: None,
};
let missed = |_| crate::cache::Known::Miss;
match fresh.plan(&wanted, &missed) {
Ok(Walked::Planned(_)) => {}
Ok(Walked::Asks(_)) => unreachable!("every key a miss"),
Err(e) => panic!("what a stream plays plans: {e}"),
}
let theirs = &mut fresh.typing;
let id = theirs.id(STREAMED).expect("the root");
let mut table = Table::new(&self.config.render.profile);
let root = table.grow(theirs, id, &BTreeMap::new()).expect("a table");
let mut out = Vec::new();
typings(mine, theirs, &mut out);
let at = self.driver.table.root;
tables(
(&self.driver.table, at, mine),
(&table, root, theirs),
&mut out,
);
Some(out)
}
}
fn typings(mine: &Typing, theirs: &Typing, out: &mut Vec<String>) {
let paths = |tys: &Typing| -> Vec<String> { tys.paths().map(|(p, _)| p.to_string()).collect() };
if paths(mine) != paths(theirs) || mine.len() != theirs.len() {
out.push(format!("typed {} nodes, not {}", mine.len(), theirs.len()));
}
for (path, id) in theirs.paths() {
let Some(held) = mine.id(path) else {
continue;
};
let same = mine.ty(held) == theirs.ty(id)
&& mine.grid(held) == theirs.grid(id)
&& refs::identity(mine, held).ok() == refs::identity(theirs, id).ok();
if !same {
out.push(format!("{path} typed otherwise"));
}
}
}
fn tables(
(a, a_root, a_tys): (&Table, usize, &Typing),
(b, b_root, b_tys): (&Table, usize, &Typing),
out: &mut Vec<String>,
) {
let (mine, theirs) = (reached(a, a_root), reached(b, b_root));
let cuts = |table: &Table, tys: &Typing| {
let mut cuts = table.pruned(tys).cuts;
cuts.sort();
cuts
};
let shape = |table: &Table, tys: &Typing, held: &BTreeMap<_, usize>, root: usize| {
(
held.len(),
cuts(table, tys),
table.moved(),
table.values[root].key,
)
};
let (x, y) = (
shape(a, a_tys, &mine, a_root),
shape(b, b_tys, &theirs, b_root),
);
if x != y {
out.push(format!("a table of {x:?}, not {y:?}"));
return;
}
for (key, y) in &theirs {
let Some(x) = mine.get(key) else {
out.push(format!("{} built otherwise", b.values[*y].name));
continue;
};
let (x, y) = (&a.values[*x], &b.values[*y]);
let keys = |table: &Table, v: &Value| -> Vec<_> {
v.reads.iter().map(|r| table.values[*r].key).collect()
};
let named = |tys: &Typing, v: &Value| v.node.map(|id| tys.name(id).to_string());
let same = (
&x.name,
x.width,
x.period,
&x.switches,
x.whole,
&x.label,
x.moved,
) == (
&y.name,
y.width,
y.period,
&y.switches,
y.whole,
&y.label,
y.moved,
) && keys(a, x) == keys(b, y)
&& named(a_tys, x) == named(b_tys, y)
&& kind(&x.kind) == kind(&y.kind);
if !same {
out.push(format!("{} built otherwise", y.name));
}
}
}
fn reached(table: &Table, root: usize) -> BTreeMap<crate::render::table::Key, usize> {
let (mut held, mut open) = (BTreeMap::new(), vec![root]);
while let Some(at) = open.pop() {
let value = &table.values[at];
if held.insert(value.key, at).is_none() {
open.extend(value.reads.iter().copied());
}
}
held
}
fn kind(kind: &Kind) -> String {
match kind {
Kind::Rows(_) => "rows".to_string(),
Kind::Program(program) => format!(
"{:?} {:?} {} {:?}",
program.renderer, program.start, program.own, program.alias
),
Kind::Frames { window, hop } => format!("frames {window} {hop}"),
Kind::Istft => "istft".to_string(),
Kind::Spectrum(_) => "spectrum".to_string(),
Kind::Resident(stored) => format!("stored {}", stored.key),
}
}