use std::collections::BTreeMap;
use minerva::kairos::{Clock, TickCounter};
use minerva::metis::{Anchor, Composer, Dot, DotSet, Locus, Retired, Rhapsody};
struct Author {
composer: Composer<Rhapsody>,
clock: Clock<TickCounter>,
glyphs: BTreeMap<Dot, char>,
}
impl Author {
fn new(station: u32) -> Self {
Self {
composer: Composer::new(station),
clock: Clock::with_default_config(TickCounter::new(), station)
.expect("a fresh station id is valid"),
glyphs: BTreeMap::new(),
}
}
fn append(&mut self, glyph: char) -> Dot {
let visible = self.composer.state().store().order();
let anchor = visible
.last()
.map_or(Anchor::Origin, |&dot| Anchor::After(dot.into()));
let rank = self.clock.now(0u16);
let (dot, _delta) = self.composer.compose(|assigned| {
let mut rhapsody = Rhapsody::new();
assert!(rhapsody.weave(assigned, Locus { anchor, rank }));
(rhapsody, DotSet::new())
});
let _ = self.glyphs.insert(dot, glyph);
dot
}
fn erase(&mut self, dot: Dot) {
let mut superseded = DotSet::new();
assert!(superseded.insert(dot));
let _ = self.composer.retract(superseded);
}
fn pull_from(&mut self, other: &Self, other_station: u32) {
let mine = self.composer.state().context().clone();
let owed = other.composer.owed_to(&mine);
let _ = self.composer.absorb(other_station, &owed);
for (&dot, &glyph) in &other.glyphs {
let _ = self.glyphs.entry(dot).or_insert(glyph);
}
}
fn reading(&self) -> String {
self.composer
.state()
.store()
.order()
.into_iter()
.map(|dot| self.glyphs[&dot])
.collect()
}
}
#[test]
fn test_day_one_two_authors_one_document() {
let mut ada = Author::new(1);
let mut bea = Author::new(2);
let h = ada.append('h');
let i = ada.append('i');
assert_eq!(ada.reading(), "hi");
bea.pull_from(&ada, 1);
assert_eq!(bea.reading(), "hi");
let a = ada.append('a');
let bang = bea.append('!');
ada.pull_from(&bea, 2);
bea.pull_from(&ada, 1);
assert_eq!(
ada.composer.state().store().order(),
bea.composer.state().store().order(),
"one document, whatever order the network chose",
);
let converged = ada.reading();
assert_eq!(converged.len(), 4);
assert!(
converged.starts_with("hi"),
"the shared prefix holds: {converged}"
);
assert_eq!(bea.reading(), converged);
assert_eq!(
ada.composer.state().store().to_bytes(),
bea.composer.state().store().to_bytes(),
);
ada.erase(a);
bea.pull_from(&ada, 1);
assert_eq!(ada.reading(), "hi!");
assert_eq!(bea.reading(), "hi!");
assert_eq!(ada.composer.state().store().skeleton_len(), 4);
let invisible_everywhere = [&ada, &bea]
.iter()
.all(|author| !author.composer.state().store().is_visible(a));
assert!(invisible_everywhere);
let mut retired = DotSet::new();
assert!(retired.insert(a));
let retired = Retired::trust(retired);
for author in [&mut ada, &mut bea] {
let before = author.composer.state().store().order();
let excised = author.composer.condense(&retired);
assert_eq!(excised, 1, "exactly the witnessed tombstone is excised");
let store = author.composer.state().store();
assert!(store.locus(a).is_none(), "the locus is gone");
assert_eq!(store.skeleton_len(), 3);
assert_eq!(store.order(), before, "condense never changes the reading");
}
assert_eq!(ada.reading(), "hi!");
assert_eq!(
ada.composer.state().store().to_bytes(),
bea.composer.state().store().to_bytes(),
"after the same honest excision, byte-identical again",
);
let final_order: Vec<Dot> = ada.composer.state().store().order();
assert_eq!(final_order, vec![h, i, bang]);
}
#[test]
fn test_the_narrative_listings_are_verbatim_from_this_file() {
let narrative = include_str!("../docs/day-one.adoc");
let source = include_str!("day_one.rs");
let source_lines: Vec<&str> = source.lines().map(str::trim).collect();
let mut blocks = 0usize;
let mut lines = narrative.lines();
while let Some(line) = lines.next() {
if line.trim() != "[source,rust]" {
continue;
}
assert_eq!(
lines.next().map(str::trim),
Some("----"),
"a source block opens with its fence",
);
let block: Vec<&str> = lines
.by_ref()
.take_while(|candidate| candidate.trim() != "----")
.map(str::trim)
.collect();
assert!(!block.is_empty(), "a narrative listing is never empty");
blocks += 1;
let quoted = source_lines
.windows(block.len())
.any(|window| window == block.as_slice());
assert!(
quoted,
"narrative listing {blocks} is not a verbatim run of tests/day_one.rs:\n{}",
block.join("\n"),
);
}
assert_eq!(blocks, 6, "the narrative quotes six listings");
}