use ulid::Ulid; use super::ids::AccountId; #[derive(Debug, thiserror::Error)] pub enum PathError { #[error("path is empty")] Empty, #[error("invalid ULID segment: {0}")] BadSegment(String), } #[derive(Clone)] pub struct MaterializedPath(String); impl MaterializedPath { pub fn parse(s: &str) -> Result { if s.is_empty() { return Err(PathError::Empty); } for seg in s.split('/') { Ulid::from_string(seg).map_err(|_| PathError::BadSegment(seg.to_string()))?; } Ok(MaterializedPath(s.to_string())) } pub fn as_str(&self) -> &str { &self.0 } pub fn depth(&self) -> usize { self.0.split('/').count() } pub fn is_ancestor_of(&self, other: &MaterializedPath) -> bool { other.0.starts_with(&self.0) && other.0.as_bytes().get(self.0.len()) == Some(&b'/') } pub fn with_child(&self, id: AccountId) -> MaterializedPath { MaterializedPath(format!("{}/{}", self.0, id.as_ulid())) } pub fn root(id: AccountId) -> MaterializedPath { MaterializedPath(id.as_ulid().to_string()) } }