2025-11-11 12:15:20 +02:00
|
|
|
use std::{fmt::Display, str::FromStr};
|
2025-11-07 16:21:14 +02:00
|
|
|
|
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct ChainIndex(Vec<u32>);
|
|
|
|
|
|
2025-11-11 12:15:20 +02:00
|
|
|
#[derive(thiserror::Error, Debug)]
|
|
|
|
|
pub enum ChainIndexError {
|
|
|
|
|
#[error("No root found")]
|
|
|
|
|
NoRootFound,
|
|
|
|
|
#[error("Failed to parse segment into a number")]
|
|
|
|
|
ParseIntError(#[from] std::num::ParseIntError),
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-07 16:21:14 +02:00
|
|
|
impl FromStr for ChainIndex {
|
2025-11-11 12:15:20 +02:00
|
|
|
type Err = ChainIndexError;
|
2025-11-07 16:21:14 +02:00
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
2025-11-26 07:07:58 +02:00
|
|
|
if !s.starts_with('/') {
|
|
|
|
|
return Err(ChainIndexError:NoRootFound);
|
2025-11-07 16:21:14 +02:00
|
|
|
}
|
2025-11-26 07:07:58 +02:00
|
|
|
|
|
|
|
|
s
|
|
|
|
|
.split("/")
|
|
|
|
|
.map(u32::from_str)
|
|
|
|
|
.collect()
|
|
|
|
|
.map_err(Into::into)
|
2025-11-07 16:21:14 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-11 12:15:20 +02:00
|
|
|
impl Display for ChainIndex {
|
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
|
write!(f, "/")?;
|
|
|
|
|
for cci in &self.0[..(self.0.len() - 1)] {
|
|
|
|
|
write!(f, "{cci}/")?;
|
2025-11-07 16:21:14 +02:00
|
|
|
}
|
2025-11-11 12:15:20 +02:00
|
|
|
write!(f, "{}", self.0.last().unwrap())
|
2025-11-07 16:21:14 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ChainIndex {
|
|
|
|
|
pub fn root() -> Self {
|
2025-11-11 12:15:20 +02:00
|
|
|
ChainIndex::from_str("/").unwrap()
|
2025-11-07 16:21:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn chain(&self) -> &[u32] {
|
|
|
|
|
&self.0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn next_in_line(&self) -> ChainIndex {
|
|
|
|
|
let mut chain = self.0.clone();
|
|
|
|
|
//ToDo: Add overflow check
|
|
|
|
|
if let Some(last_p) = chain.last_mut() {
|
|
|
|
|
*last_p += 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ChainIndex(chain)
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-26 07:07:58 +02:00
|
|
|
pub fn nth_child(&self, child_id: u32) -> ChainIndex {
|
2025-11-07 16:21:14 +02:00
|
|
|
let mut chain = self.0.clone();
|
|
|
|
|
chain.push(child_id);
|
|
|
|
|
|
|
|
|
|
ChainIndex(chain)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_chain_id_root_correct() {
|
|
|
|
|
let chain_id = ChainIndex::root();
|
2025-11-11 12:15:20 +02:00
|
|
|
let chain_id_2 = ChainIndex::from_str("/").unwrap();
|
2025-11-07 16:21:14 +02:00
|
|
|
|
|
|
|
|
assert_eq!(chain_id, chain_id_2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_chain_id_deser_correct() {
|
2025-11-11 12:15:20 +02:00
|
|
|
let chain_id = ChainIndex::from_str("/257").unwrap();
|
2025-11-07 16:21:14 +02:00
|
|
|
|
|
|
|
|
assert_eq!(chain_id.chain(), &[257]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_chain_id_next_in_line_correct() {
|
2025-11-11 12:15:20 +02:00
|
|
|
let chain_id = ChainIndex::from_str("/257").unwrap();
|
2025-11-07 16:21:14 +02:00
|
|
|
let next_in_line = chain_id.next_in_line();
|
|
|
|
|
|
2025-11-11 12:15:20 +02:00
|
|
|
assert_eq!(next_in_line, ChainIndex::from_str("/258").unwrap());
|
2025-11-07 16:21:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_chain_id_child_correct() {
|
2025-11-11 12:15:20 +02:00
|
|
|
let chain_id = ChainIndex::from_str("/257").unwrap();
|
2025-11-07 16:21:14 +02:00
|
|
|
let child = chain_id.n_th_child(3);
|
|
|
|
|
|
2025-11-11 12:15:20 +02:00
|
|
|
assert_eq!(child, ChainIndex::from_str("/257/3").unwrap());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_correct_display() {
|
|
|
|
|
let chainid = ChainIndex(vec![5, 7, 8]);
|
|
|
|
|
|
|
|
|
|
let string_index = format!("{chainid}");
|
|
|
|
|
|
|
|
|
|
assert_eq!(string_index, "/5/7/8".to_string());
|
2025-11-07 16:21:14 +02:00
|
|
|
}
|
|
|
|
|
}
|