use std::{
fmt,
ops::ControlFlow::{self, Break, Continue},
ptr,
};
use indexmap::IndexMap;
use ltk_hash::BinHash;
use super::{Interrupt, NodeRef, Resume, Trail, TrailSegment, TreeValue as _, Visit, WalkOutcome};
use crate::{property::values, Bin, BinObject, BinOverride, Error, PropertyValueEnum};
#[expect(
unused_variables,
reason = "the defaults name their parameters for the reader and use none of them"
)]
pub trait VisitorMut {
type Error: From<Error>;
fn enter_node(&mut self, node: &mut NodeRefMut<'_>) -> Result<Visit, Self::Error> {
Ok(Visit::Continue)
}
fn exit_node(&mut self, node: &mut NodeRefMut<'_>) -> Result<Visit, Self::Error> {
Ok(Visit::Continue)
}
fn enter_property(&mut self, property: &mut PropertyRefMut<'_>) -> Result<Visit, Self::Error> {
Ok(Visit::Continue)
}
fn exit_property(&mut self, property: &mut PropertyRefMut<'_>) -> Result<Visit, Self::Error> {
Ok(Visit::Continue)
}
}
impl<W: VisitorMut + ?Sized> VisitorMut for &mut W {
type Error = W::Error;
fn enter_node(&mut self, node: &mut NodeRefMut<'_>) -> Result<Visit, Self::Error> {
(**self).enter_node(node)
}
fn exit_node(&mut self, node: &mut NodeRefMut<'_>) -> Result<Visit, Self::Error> {
(**self).exit_node(node)
}
fn enter_property(&mut self, property: &mut PropertyRefMut<'_>) -> Result<Visit, Self::Error> {
(**self).enter_property(property)
}
fn exit_property(&mut self, property: &mut PropertyRefMut<'_>) -> Result<Visit, Self::Error> {
(**self).exit_property(property)
}
}
pub struct NodeRefMut<'t> {
object_hash: BinHash,
class_hash: BinHash,
properties: &'t mut IndexMap<BinHash, PropertyValueEnum>,
trail: &'t Trail<&'t PropertyValueEnum>,
}
impl<'t> NodeRefMut<'t> {
#[must_use]
pub fn object_hash(&self) -> BinHash {
self.object_hash
}
#[must_use]
pub fn class_hash(&self) -> BinHash {
self.class_hash
}
#[must_use]
pub fn trail(&self) -> &'t Trail<&'t PropertyValueEnum> {
self.trail
}
#[must_use]
pub fn is_root(&self) -> bool {
self.trail.is_empty()
}
#[must_use]
pub fn inner(&self) -> NodeRef<'_> {
NodeRef::new(self.class_hash, self.properties)
}
#[must_use]
pub fn properties(&self) -> &IndexMap<BinHash, PropertyValueEnum> {
self.properties
}
#[must_use]
pub fn properties_mut(&mut self) -> &mut IndexMap<BinHash, PropertyValueEnum> {
self.properties
}
}
impl fmt::Debug for NodeRefMut<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("NodeRefMut")
.field("object_hash", &self.object_hash)
.field("class_hash", &self.class_hash)
.field("property_count", &self.properties.len())
.field("trail", &format_args!("{}", self.trail))
.finish()
}
}
pub struct PropertyRefMut<'t> {
object_hash: BinHash,
node_class_hash: BinHash,
field: BinHash,
value: &'t mut PropertyValueEnum,
trail: &'t Trail<&'t PropertyValueEnum>,
}
impl<'t> PropertyRefMut<'t> {
#[must_use]
pub fn object_hash(&self) -> BinHash {
self.object_hash
}
#[must_use]
pub fn node_class_hash(&self) -> BinHash {
self.node_class_hash
}
#[must_use]
pub fn field(&self) -> BinHash {
self.field
}
#[must_use]
pub fn trail(&self) -> &'t Trail<&'t PropertyValueEnum> {
self.trail
}
#[must_use]
pub fn value(&self) -> &PropertyValueEnum {
self.value
}
#[must_use]
pub fn value_mut(&mut self) -> &mut PropertyValueEnum {
self.value
}
}
impl fmt::Debug for PropertyRefMut<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PropertyRefMut")
.field("object_hash", &self.object_hash)
.field("node_class_hash", &self.node_class_hash)
.field("field", &self.field)
.field("kind", &self.value.kind())
.field("trail", &format_args!("{}", self.trail))
.finish()
}
}
struct WalkerMut<'w> {
object_hash: BinHash,
trail: Trail<&'w PropertyValueEnum>,
}
impl<'w> WalkerMut<'w> {
fn new() -> Self {
Self {
object_hash: BinHash(0),
trail: Trail::new(),
}
}
fn walk_object<W: VisitorMut>(
&mut self,
object: &'w mut BinObject,
visitor: &mut W,
) -> Result<WalkOutcome, W::Error> {
self.object_hash = object.path_hash;
self.trail.clear();
Ok(
match self.walk_node(object.class_hash, &mut object.properties, visitor)? {
Continue(_) => WalkOutcome::Completed,
Break(Interrupt::Unwind) => WalkOutcome::Stopped,
Break(Interrupt::Abort) => WalkOutcome::Aborted,
},
)
}
fn node<'t>(
&'t self,
class_hash: BinHash,
properties: &'t mut IndexMap<BinHash, PropertyValueEnum>,
) -> NodeRefMut<'t> {
NodeRefMut {
object_hash: self.object_hash,
class_hash,
properties,
trail: &self.trail,
}
}
fn property<'t>(
&'t self,
node_class_hash: BinHash,
field: BinHash,
value: &'t mut PropertyValueEnum,
) -> PropertyRefMut<'t> {
PropertyRefMut {
object_hash: self.object_hash,
node_class_hash,
field,
value,
trail: &self.trail,
}
}
fn walk_node<W: VisitorMut>(
&mut self,
class_hash: BinHash,
properties: &mut IndexMap<BinHash, PropertyValueEnum>,
visitor: &mut W,
) -> Result<ControlFlow<Interrupt, Resume>, W::Error> {
let walked = match visitor.enter_node(&mut self.node(class_hash, properties))? {
Visit::Abort => return Ok(Break(Interrupt::Abort)),
Visit::Stop => Break(Interrupt::Unwind),
Visit::Skip => Continue(()),
Visit::Continue => self.walk_properties(class_hash, properties, visitor)?,
};
if let Break(Interrupt::Abort) = walked {
return Ok(Break(Interrupt::Abort));
}
Ok(
match (
walked,
visitor.exit_node(&mut self.node(class_hash, properties))?,
) {
(_, 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: VisitorMut>(
&mut self,
class_hash: BinHash,
properties: &mut IndexMap<BinHash, PropertyValueEnum>,
visitor: &mut W,
) -> Result<ControlFlow<Interrupt>, W::Error> {
for (&field, value) in properties.iter_mut() {
let visit = visitor.enter_property(&mut self.property(class_hash, field, value))?;
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, 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(&mut self.property(class_hash, field, value))?,
) {
(_, 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: VisitorMut>(
&mut self,
value: &mut PropertyValueEnum,
visitor: &mut W,
) -> Result<ControlFlow<Interrupt>, W::Error> {
match value {
PropertyValueEnum::Struct(_) | PropertyValueEnum::Embedded(_) => {
let Some(node) = as_node_mut(value) else {
return Ok(Continue(()));
};
Ok(
match self.walk_node(node.class_hash, &mut node.properties, visitor)? {
Break(interrupt) => Break(interrupt),
Continue(_) => Continue(()),
},
)
}
PropertyValueEnum::Container(items) => self.descend_items(items.items_mut(), visitor),
PropertyValueEnum::UnorderedContainer(items) => {
self.descend_items(items.0.items_mut(), visitor)
}
PropertyValueEnum::Optional(optional) => match optional.slot() {
Some(slot) => self.descend_items(std::iter::once(slot.into_inner()), visitor),
None => Ok(Continue(())),
},
PropertyValueEnum::Map(map) => {
for (key, value) in map.entries_mut() {
let Some(node) = as_node_mut(value) else {
continue;
};
let key: &'w PropertyValueEnum = unsafe { &*ptr::from_ref(key) };
self.trail.push(TrailSegment::Key(key));
let walked = self.walk_node(node.class_hash, &mut node.properties, visitor);
self.trail.pop();
match walked? {
Break(interrupt) => return Ok(Break(interrupt)),
Continue(Resume::Parent) => break,
Continue(Resume::Siblings) => {}
}
}
Ok(Continue(()))
}
_ => Ok(Continue(())),
}
}
fn descend_items<'i, W: VisitorMut>(
&mut self,
items: impl Iterator<Item = &'i mut PropertyValueEnum>,
visitor: &mut W,
) -> Result<ControlFlow<Interrupt>, W::Error> {
for (index, item) in items.enumerate() {
let Some(node) = as_node_mut(item) else {
continue;
};
self.trail.push(TrailSegment::Index(index));
let walked = self.walk_node(node.class_hash, &mut node.properties, visitor);
self.trail.pop();
match walked? {
Break(interrupt) => return Ok(Break(interrupt)),
Continue(Resume::Parent) => break,
Continue(Resume::Siblings) => {}
}
}
Ok(Continue(()))
}
}
fn as_node_mut(value: &mut PropertyValueEnum) -> Option<&mut values::Struct> {
let node = match value {
PropertyValueEnum::Struct(node) => node,
PropertyValueEnum::Embedded(embedded) => &mut embedded.0,
_ => return None,
};
(*node.class_hash != 0).then_some(node)
}
fn walk_all_mut<'w, W: VisitorMut>(
objects: impl IntoIterator<Item = &'w mut BinObject>,
visitor: &mut W,
) -> Result<WalkOutcome, W::Error> {
let mut walker = WalkerMut::new();
for object in objects {
match walker.walk_object(object, visitor)? {
WalkOutcome::Completed => {}
ended => return Ok(ended),
}
}
Ok(WalkOutcome::Completed)
}
impl BinObject {
pub fn walk_mut<W: VisitorMut>(&mut self, visitor: &mut W) -> Result<WalkOutcome, W::Error> {
WalkerMut::new().walk_object(self, visitor)
}
}
impl Bin {
pub fn walk_mut<W: VisitorMut>(&mut self, visitor: &mut W) -> Result<WalkOutcome, W::Error> {
walk_all_mut(self.objects.values_mut(), visitor)
}
}
impl BinOverride {
pub fn walk_mut<W: VisitorMut>(&mut self, visitor: &mut W) -> Result<WalkOutcome, W::Error> {
walk_all_mut(self.objects.values_mut(), visitor)
}
}