use crate::error::Result;
use crate::types::{Key, KeyPrefix, Namespace};
#[must_use]
pub fn is_key_within_namespace(key: &Key, namespace: &Namespace) -> bool {
let key_str = key.as_str();
let ns = namespace.as_str();
if key_str == ns {
return true;
}
key_str
.strip_prefix(ns)
.is_some_and(|suffix| suffix.starts_with('/'))
}
pub fn join_namespace_and_leaf(namespace: &Namespace, leaf: &str) -> Result<Key> {
namespace.join(leaf)
}
#[must_use]
pub fn parent_namespace(namespace: &Namespace) -> Option<Namespace> {
namespace.parent()
}
#[must_use]
pub fn namespace_ancestors(namespace: &Namespace) -> Vec<Namespace> {
let mut current = namespace.clone();
let mut result = Vec::new();
while let Some(parent) = current.parent() {
result.push(parent.clone());
current = parent;
}
result
}
#[must_use]
pub fn namespace_depth(namespace: &Namespace) -> usize {
segment_count(namespace.as_str())
}
#[must_use]
pub fn key_depth(key: &Key) -> usize {
segment_count(key.as_str())
}
#[must_use]
pub fn namespace_leaf(namespace: &Namespace) -> &str {
namespace.leaf()
}
#[must_use]
pub fn key_leaf(key: &Key) -> &str {
key.leaf()
}
#[must_use]
pub fn namespace_prefix(namespace: &Namespace) -> KeyPrefix {
KeyPrefix::new(namespace.as_str())
.expect("validated namespace must always produce a valid prefix")
}
#[must_use]
pub fn key_matches_prefix(key: &Key, prefix: &KeyPrefix) -> bool {
prefix.matches(key)
}
#[must_use]
pub fn split_key(key: &Key) -> Option<(Namespace, String)> {
let raw = key.as_str();
raw.rsplit_once('/').map(|(prefix, leaf)| {
(
Namespace::new(prefix).expect("validated key prefix must always be a valid namespace"),
leaf.to_owned(),
)
})
}
#[must_use]
pub fn common_namespace(left: &Key, right: &Key) -> Option<Namespace> {
let left_parts: Vec<&str> = left.as_str().split('/').collect();
let right_parts: Vec<&str> = right.as_str().split('/').collect();
let mut shared = Vec::new();
for (a, b) in left_parts.iter().zip(right_parts.iter()) {
if a == b {
shared.push(*a);
} else {
break;
}
}
if shared.is_empty() {
return None;
}
let joined = shared.join("/");
Namespace::new(joined).ok()
}
#[must_use]
pub fn trim_outer_separators(input: &str) -> &str {
input.trim_matches('/')
}
#[must_use]
fn segment_count(input: &str) -> usize {
input.split('/').count()
}