mod mutable;
pub(crate) mod owned;
mod tree;
mod view;
#[cfg(test)]
mod tests;
pub use mutable::{NodeRefMut, PropertyRefMut, VisitorMut};
pub use owned::{ChildrenRef, NodeRef, PropertiesRef};
pub use tree::{ChildSegment, Declaration, Leaf, TreeKind, TreeNode, TreeValue};
pub use view::{RawValue, ViewChildren, ViewProperties};
use std::{
fmt, io,
ops::ControlFlow::{self, Break, Continue},
};
use ltk_hash::BinHash;
use crate::{
path::ValuePath,
stream::{BinStream, ObjectStream, ObjectView},
Bin, BinObject, BinOverride, Error, PropertyValueEnum,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Visit {
Abort,
Stop,
Skip,
Continue,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WalkOutcome {
Completed,
Stopped,
Aborted,
}
#[expect(
unused_variables,
reason = "the defaults name their parameters for the reader and use none of them"
)]
pub trait Visitor<'a, V: TreeValue<'a>> {
type Error: From<Error>;
fn enter_node(&mut self, node: &Node<'_, 'a, V>) -> Result<Visit, Self::Error> {
Ok(Visit::Continue)
}
fn exit_node(&mut self, node: &Node<'_, 'a, V>) -> Result<Visit, Self::Error> {
Ok(Visit::Continue)
}
fn enter_property(
&mut self,
field: BinHash,
value: V,
node: &Node<'_, 'a, V>,
) -> Result<Visit, Self::Error> {
Ok(Visit::Continue)
}
fn exit_property(
&mut self,
field: BinHash,
value: V,
node: &Node<'_, 'a, V>,
) -> Result<Visit, Self::Error> {
Ok(Visit::Continue)
}
}
impl<'a, V: TreeValue<'a>, W: Visitor<'a, V> + ?Sized> Visitor<'a, V> for &mut W {
type Error = W::Error;
fn enter_node(&mut self, node: &Node<'_, 'a, V>) -> Result<Visit, Self::Error> {
(**self).enter_node(node)
}
fn exit_node(&mut self, node: &Node<'_, 'a, V>) -> Result<Visit, Self::Error> {
(**self).exit_node(node)
}
fn enter_property(
&mut self,
field: BinHash,
value: V,
node: &Node<'_, 'a, V>,
) -> Result<Visit, Self::Error> {
(**self).enter_property(field, value, node)
}
fn exit_property(
&mut self,
field: BinHash,
value: V,
node: &Node<'_, 'a, V>,
) -> Result<Visit, Self::Error> {
(**self).exit_property(field, value, node)
}
}
pub struct Node<'t, 'a, V: TreeValue<'a>> {
object_hash: BinHash,
inner: V::Node,
trail: &'t Trail<V>,
}
impl<'t, 'a, V: TreeValue<'a>> Node<'t, 'a, V> {
fn new(object_hash: BinHash, inner: V::Node, trail: &'t Trail<V>) -> Self {
Self {
object_hash,
inner,
trail,
}
}
#[must_use]
pub fn object_hash(&self) -> BinHash {
self.object_hash
}
#[must_use]
pub fn class_hash(&self) -> BinHash {
self.inner.class_hash()
}
#[must_use]
pub fn inner(&self) -> V::Node {
self.inner
}
#[must_use]
pub fn trail(&self) -> &'t Trail<V> {
self.trail
}
#[must_use]
pub fn is_root(&self) -> bool {
self.trail.is_empty()
}
pub fn to_value_path(&self) -> Result<ValuePath, Error> {
self.trail.to_value_path()
}
}
impl<'a, V: TreeValue<'a>> Clone for Node<'_, 'a, V> {
fn clone(&self) -> Self {
*self
}
}
impl<'a, V: TreeValue<'a>> Copy for Node<'_, 'a, V> {}
impl<'a, V: TreeValue<'a>> fmt::Debug for Node<'_, 'a, V> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Node")
.field("object_hash", &self.object_hash)
.field("class_hash", &self.class_hash())
.field("trail", &format_args!("{}", self.trail))
.finish()
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum TrailSegment<V> {
Field(BinHash),
Index(usize),
Key(V),
}
#[derive(Debug)]
pub struct Trail<V> {
segments: Vec<TrailSegment<V>>,
classes: Vec<BinHash>,
}
impl<V> Trail<V> {
pub(crate) fn new() -> Self {
Self {
segments: Vec::new(),
classes: Vec::new(),
}
}
#[must_use]
pub fn segments(&self) -> &[TrailSegment<V>] {
&self.segments
}
#[must_use]
pub fn len(&self) -> usize {
self.segments.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.segments.is_empty()
}
#[must_use]
pub fn classes(&self) -> &[BinHash] {
&self.classes
}
pub(crate) fn push_field(&mut self, field: BinHash, class: BinHash) {
self.segments.push(TrailSegment::Field(field));
self.classes.push(class);
}
pub(crate) fn push(&mut self, segment: TrailSegment<V>) {
self.segments.push(segment);
}
pub(crate) fn pop(&mut self) {
if let Some(TrailSegment::Field(_)) = self.segments.pop() {
self.classes.pop();
}
}
pub(crate) fn clear(&mut self) {
self.segments.clear();
self.classes.clear();
}
}
impl<'a, V: TreeValue<'a>> Trail<V> {
pub fn to_value_path(&self) -> Result<ValuePath, Error> {
let mut path = ValuePath::new();
let mut classes = self.classes.iter();
for segment in &self.segments {
match segment {
TrailSegment::Field(field) => {
let class = classes.next().expect("the trail keeps one class per field");
path.push_field(*field, *class);
}
TrailSegment::Index(index) => path.push_index(*index),
TrailSegment::Key(key) => path.push_key(key.map_key()?),
}
}
Ok(path)
}
}
impl<'a, V: TreeValue<'a>> fmt::Display for Trail<V> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (i, segment) in self.segments.iter().enumerate() {
match segment {
TrailSegment::Field(field) => {
if i > 0 {
f.write_str(".")?;
}
write!(f, "{field:08x}")?;
}
TrailSegment::Index(index) => write!(f, "[{index}]")?,
TrailSegment::Key(key) => {
f.write_str("{")?;
match key.as_leaf() {
Ok(Some(leaf)) => leaf.write_key(f)?,
Ok(None) | Err(_) => f.write_str("?")?,
}
f.write_str("}")?;
}
}
}
Ok(())
}
}
enum Interrupt {
Unwind,
Abort,
}
enum Resume {
Siblings,
Parent,
}
struct Walker<V> {
object_hash: BinHash,
trail: Trail<V>,
}
impl<'a, V: TreeValue<'a>> Walker<V> {
fn new() -> Self {
Self {
object_hash: BinHash(0),
trail: Trail::new(),
}
}
fn walk_object<W: Visitor<'a, V>>(
&mut self,
object_hash: BinHash,
root: V::Node,
visitor: &mut W,
) -> Result<WalkOutcome, W::Error> {
self.object_hash = object_hash;
self.trail.clear();
Ok(match self.walk_node(root, visitor)? {
Continue(_) => WalkOutcome::Completed,
Break(Interrupt::Unwind) => WalkOutcome::Stopped,
Break(Interrupt::Abort) => WalkOutcome::Aborted,
})
}
fn node(&self, inner: V::Node) -> Node<'_, 'a, V> {
Node::new(self.object_hash, inner, &self.trail)
}
fn walk_node<W: Visitor<'a, V>>(
&mut self,
node: V::Node,
visitor: &mut W,
) -> Result<ControlFlow<Interrupt, Resume>, W::Error> {
let walked = match visitor.enter_node(&self.node(node))? {
Visit::Abort => return Ok(Break(Interrupt::Abort)),
Visit::Stop => Break(Interrupt::Unwind),
Visit::Skip => Continue(()),
Visit::Continue => self.walk_properties(node, visitor)?,
};
if let Break(Interrupt::Abort) = walked {
return Ok(Break(Interrupt::Abort));
}
Ok(match (walked, visitor.exit_node(&self.node(node))?) {
(_, Visit::Abort) => Break(Interrupt::Abort),
(Break(Interrupt::Unwind), _) | (_, Visit::Stop) => Break(Interrupt::Unwind),
(_, Visit::Skip) => Continue(Resume::Parent),
(_, Visit::Continue) => Continue(Resume::Siblings),
})
}
fn walk_properties<W: Visitor<'a, V>>(
&mut self,
node: V::Node,
visitor: &mut W,
) -> Result<ControlFlow<Interrupt>, W::Error> {
for property in node.properties() {
let (field, value) = property?;
let visit = visitor.enter_property(field, value, &self.node(node))?;
if !value.can_contain_node()? {
match visit {
Visit::Abort => return Ok(Break(Interrupt::Abort)),
Visit::Stop => return Ok(Break(Interrupt::Unwind)),
Visit::Skip | Visit::Continue => continue,
}
}
let walked = match visit {
Visit::Abort => return Ok(Break(Interrupt::Abort)),
Visit::Stop => Break(Interrupt::Unwind),
Visit::Skip => Continue(()),
Visit::Continue => {
self.trail.push_field(field, node.class_hash());
let walked = self.descend(value, visitor);
self.trail.pop();
walked?
}
};
if let Break(Interrupt::Abort) = walked {
return Ok(Break(Interrupt::Abort));
}
match (
walked,
visitor.exit_property(field, value, &self.node(node))?,
) {
(_, Visit::Abort) => return Ok(Break(Interrupt::Abort)),
(Break(Interrupt::Unwind), _) | (_, Visit::Stop) => {
return Ok(Break(Interrupt::Unwind))
}
(_, Visit::Skip) => break,
(_, Visit::Continue) => {}
}
}
Ok(Continue(()))
}
fn descend<W: Visitor<'a, V>>(
&mut self,
value: V,
visitor: &mut W,
) -> Result<ControlFlow<Interrupt>, W::Error> {
if let Some(node) = value.as_node()? {
return Ok(match self.walk_node(node, visitor)? {
Break(interrupt) => Break(interrupt),
Continue(_) => Continue(()),
});
}
for child in value.children()? {
let (segment, item) = child?;
let Some(node) = item.as_node()? else {
continue;
};
self.trail.push(match segment {
ChildSegment::Index(index) => TrailSegment::Index(index),
ChildSegment::Key(key) => TrailSegment::Key(key),
});
let walked = self.walk_node(node, visitor);
self.trail.pop();
match walked? {
Break(interrupt) => return Ok(Break(interrupt)),
Continue(Resume::Parent) => break,
Continue(Resume::Siblings) => {}
}
}
Ok(Continue(()))
}
}
fn walk_all<'a, V, W, N>(
objects: impl IntoIterator<Item = (BinHash, N)>,
visitor: &mut W,
) -> Result<WalkOutcome, W::Error>
where
V: TreeValue<'a, Node = N>,
N: TreeNode<'a, Value = V>,
W: Visitor<'a, V>,
{
let mut walker = Walker::new();
for (object_hash, root) in objects {
match walker.walk_object(object_hash, root, visitor)? {
WalkOutcome::Completed => {}
ended => return Ok(ended),
}
}
Ok(WalkOutcome::Completed)
}
impl BinObject {
pub fn walk<'a, W>(&'a self, visitor: &mut W) -> Result<WalkOutcome, W::Error>
where
W: Visitor<'a, &'a PropertyValueEnum>,
{
Walker::new().walk_object(self.path_hash, NodeRef::from(self), visitor)
}
}
impl Bin {
pub fn walk<'a, W>(&'a self, visitor: &mut W) -> Result<WalkOutcome, W::Error>
where
W: Visitor<'a, &'a PropertyValueEnum>,
{
walk_all(
self.objects
.values()
.map(|object| (object.path_hash, NodeRef::from(object))),
visitor,
)
}
}
impl BinOverride {
pub fn walk<'a, W>(&'a self, visitor: &mut W) -> Result<WalkOutcome, W::Error>
where
W: Visitor<'a, &'a PropertyValueEnum>,
{
walk_all(
self.objects
.values()
.map(|object| (object.path_hash, NodeRef::from(object))),
visitor,
)
}
}
impl<'a> ObjectView<'a> {
pub fn walk<W>(&self, visitor: &mut W) -> Result<WalkOutcome, W::Error>
where
W: Visitor<'a, RawValue<'a>>,
{
Walker::new().walk_object(self.path_hash(), self.as_struct(), visitor)
}
}
impl<R: io::Read + io::Seek> ObjectStream<'_, R> {
pub fn walk<E, W>(&mut self, visitor: &mut W) -> Result<WalkOutcome, E>
where
E: From<Error>,
W: for<'a> Visitor<'a, RawValue<'a>, Error = E>,
{
self.view()?.walk(visitor)
}
}
impl<R: io::Read + io::Seek> BinStream<R> {
pub fn walk<E, W>(&mut self, visitor: &mut W) -> Result<WalkOutcome, E>
where
E: From<Error>,
W: for<'a> Visitor<'a, RawValue<'a>, Error = E>,
{
let mut objects = self.objects();
while let Some(mut object) = objects.next()? {
match object.walk(visitor)? {
WalkOutcome::Completed => {}
ended => return Ok(ended),
}
}
Ok(WalkOutcome::Completed)
}
}