use super::*;
use std::collections::{HashMap, HashSet};
mod branches;
mod leaf;
mod nodes;
mod relink;
mod view;
mod walk;
pub(crate) use nodes::Builder;
pub(crate) use nodes::ChainNode;
pub use view::{ChainView, TurnStamp};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum LeafSource {
LastPrompt,
Explicit,
#[default]
Tail,
TailLeafAbsent,
Cleared,
}
impl LeafSource {
#[must_use]
pub fn as_str(self) -> &'static str {
match self {
LeafSource::LastPrompt => "last-prompt",
LeafSource::Explicit => "explicit",
LeafSource::Tail => "tail",
LeafSource::TailLeafAbsent => "tail (last-prompt leaf absent)",
LeafSource::Cleared => "newest (leaf set cleared)",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Survival {
#[default]
Live,
PreCut,
Abandoned { root: usize },
}
impl Survival {
#[must_use]
pub fn as_str(self) -> &'static str {
match self {
Survival::Live => "live",
Survival::PreCut => "pre-cut",
Survival::Abandoned { .. } => "abandoned",
}
}
#[must_use]
pub fn selectable(self) -> bool {
!matches!(self, Survival::Abandoned { .. })
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Kind {
Draft { superseded_by: Option<usize> },
Rewound { resend: Option<usize> },
}
impl Kind {
#[must_use]
pub fn survivor(self) -> Option<usize> {
match self {
Kind::Draft { superseded_by } => superseded_by,
Kind::Rewound { resend } => resend,
}
}
#[must_use]
pub fn class(self) -> Class {
match self {
Kind::Draft { .. } => Class::UserUnsent,
Kind::Rewound { .. } => Class::UserRewound,
}
}
}
#[derive(Debug, Default)]
pub struct Chain {
survival: Vec<Survival>,
kinds: HashMap<usize, Kind>,
replay_of: HashMap<usize, usize>,
rewound_branch: HashSet<usize>,
#[allow(dead_code)]
pub leaf_index: Option<usize>,
pub leaf_source: LeafSource,
pub boundary_cut: Option<usize>,
#[allow(dead_code)]
pub floor: usize,
pub abandoned_records: usize,
pub rewound_turns: usize,
pub drafts: usize,
pub replay_copies: usize,
}
impl Chain {
#[allow(dead_code)]
#[must_use]
pub fn build(records: &[Record], leaf_hint: Option<&str>) -> Chain {
Self::build_by(records, |r| ChainNode::Full(r), leaf_hint)
}
#[must_use]
pub fn build_by<T>(
records: &[T],
node: impl Fn(&T) -> ChainNode<'_>,
leaf_hint: Option<&str>,
) -> Chain {
let mut b = Builder::new(records, &node);
let anchor = relink::apply(&mut b);
b.index_children();
let (leaf, source) = leaf::pick(&b, leaf_hint, anchor);
let out = walk::run(&mut b, leaf);
branches::finish(b, leaf, source, out)
}
#[must_use]
pub fn survival(&self, i: usize) -> Survival {
self.survival.get(i).copied().unwrap_or_default()
}
#[must_use]
pub fn kind(&self, i: usize) -> Option<Kind> {
self.kinds.get(&i).copied()
}
#[must_use]
pub fn replay_of(&self, i: usize) -> Option<usize> {
self.replay_of.get(&i).copied()
}
#[must_use]
pub fn on_rewound_branch(&self, i: usize) -> bool {
self.rewound_branch.contains(&i)
}
#[must_use]
pub fn opens(&self, i: usize) -> bool {
self.survival(i).selectable() && !self.replay_of.contains_key(&i)
}
#[must_use]
pub fn opener_class(&self, i: usize) -> Option<Class> {
self.kinds.get(&i).map(|k| k.class())
}
#[must_use]
pub fn superseding(&self, i: usize) -> Option<usize> {
self.kinds.get(&i).and_then(|k| k.survivor())
}
#[must_use]
pub fn abandoned_root(&self, i: usize) -> Option<usize> {
match self.survival(i) {
Survival::Abandoned { root } => Some(root),
_ => None,
}
}
}