use std::fmt;
use super::TreeError;
use crate::NodeAddr;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CoordEntry {
pub node_addr: NodeAddr,
pub sequence: u64,
pub timestamp: u64,
}
impl CoordEntry {
pub const WIRE_SIZE: usize = 32;
pub fn new(node_addr: NodeAddr, sequence: u64, timestamp: u64) -> Self {
Self {
node_addr,
sequence,
timestamp,
}
}
pub fn addr_only(node_addr: NodeAddr) -> Self {
Self {
node_addr,
sequence: 0,
timestamp: 0,
}
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct TreeCoordinate(Vec<CoordEntry>);
impl TreeCoordinate {
pub fn new(path: Vec<CoordEntry>) -> Result<Self, TreeError> {
if path.is_empty() {
return Err(TreeError::EmptyCoordinate);
}
Ok(Self(path))
}
pub fn from_addrs(addrs: Vec<NodeAddr>) -> Result<Self, TreeError> {
if addrs.is_empty() {
return Err(TreeError::EmptyCoordinate);
}
Ok(Self(addrs.into_iter().map(CoordEntry::addr_only).collect()))
}
pub fn root(node_addr: NodeAddr) -> Self {
Self(vec![CoordEntry::addr_only(node_addr)])
}
pub fn root_with_meta(node_addr: NodeAddr, sequence: u64, timestamp: u64) -> Self {
Self(vec![CoordEntry::new(node_addr, sequence, timestamp)])
}
pub fn node_addr(&self) -> &NodeAddr {
&self.0[0].node_addr
}
pub fn root_id(&self) -> &NodeAddr {
&self.0.last().expect("coordinate never empty").node_addr
}
pub fn parent_id(&self) -> &NodeAddr {
self.0
.get(1)
.map(|e| &e.node_addr)
.unwrap_or(&self.0[0].node_addr)
}
pub fn depth(&self) -> usize {
self.0.len() - 1
}
pub fn entries(&self) -> &[CoordEntry] {
&self.0
}
pub fn node_addrs(&self) -> impl DoubleEndedIterator<Item = &NodeAddr> {
self.0.iter().map(|e| &e.node_addr)
}
pub fn is_root(&self) -> bool {
self.0.len() == 1
}
pub fn distance_to(&self, other: &TreeCoordinate) -> usize {
if self.root_id() != other.root_id() {
return usize::MAX;
}
let lca_depth = self.lca_depth(other);
let self_to_lca = self.depth() - lca_depth;
let other_to_lca = other.depth() - lca_depth;
self_to_lca + other_to_lca
}
pub fn lca_depth(&self, other: &TreeCoordinate) -> usize {
let mut common: usize = 0;
let self_rev = self.node_addrs().rev();
let other_rev = other.node_addrs().rev();
for (a, b) in self_rev.zip(other_rev) {
if a == b {
common += 1;
} else {
break;
}
}
common.saturating_sub(1)
}
pub fn lca(&self, other: &TreeCoordinate) -> Option<&NodeAddr> {
let self_rev: Vec<_> = self.node_addrs().rev().collect();
let other_rev: Vec<_> = other.node_addrs().rev().collect();
let mut lca = None;
for (a, b) in self_rev.iter().zip(other_rev.iter()) {
if a == b {
lca = Some(*a);
} else {
break;
}
}
lca
}
pub fn has_ancestor(&self, other: &NodeAddr) -> bool {
self.node_addrs().skip(1).any(|id| id == other)
}
pub fn contains(&self, other: &NodeAddr) -> bool {
self.node_addrs().any(|id| id == other)
}
pub fn ancestor_at(&self, depth: usize) -> Option<&NodeAddr> {
self.0.get(depth).map(|e| &e.node_addr)
}
}
impl fmt::Debug for TreeCoordinate {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "TreeCoordinate(depth={}, path=[", self.depth())?;
for (i, entry) in self.0.iter().enumerate() {
if i > 0 {
write!(f, " → ")?;
}
write!(
f,
"{:02x}{:02x}",
entry.node_addr.as_bytes()[0],
entry.node_addr.as_bytes()[1]
)?;
}
write!(f, "])")
}
}