use borsh::{BorshDeserialize, BorshSerialize};
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::fmt::{Error, Formatter};
#[cfg(feature = "openapi")]
use utoipa::ToSchema;
#[cfg(feature = "typescript")]
use ts_rs::TS;
#[derive(
Clone,
Hash,
Eq,
PartialEq,
PartialOrd,
Ord,
Serialize,
Deserialize,
BorshDeserialize,
BorshSerialize,
)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
#[cfg_attr(feature = "typescript", derive(TS))]
#[cfg_attr(feature = "typescript", ts(export))]
pub struct Namespace(Vec<String>);
impl Namespace {
pub const fn new() -> Self {
Self(Vec::new())
}
pub fn check(&self) -> bool {
!self
.0
.iter()
.any(|x| x.trim().is_empty() || x.len() > 100 || x != x.trim())
}
pub fn add(&mut self, name: &str) {
let name = name.trim();
if !name.is_empty() {
self.0.push(name.to_owned())
}
}
pub fn root(&self) -> Self {
if self.0.len() == 1 {
self.clone()
} else if !self.0.is_empty() {
Self(self.0.iter().take(1).cloned().collect())
} else {
Self(Vec::new())
}
}
pub fn parent(&self) -> Self {
if self.0.len() > 1 {
let mut tokens = self.0.clone();
tokens.truncate(tokens.len() - 1);
Self(tokens)
} else {
Self(Vec::new())
}
}
pub fn key(&self) -> String {
self.0.last().cloned().unwrap_or_else(|| "".to_string())
}
pub const fn level(&self) -> usize {
self.0.len()
}
pub fn at_level(&self, level: usize) -> Self {
if level == 0 || level > self.level() {
self.clone()
} else {
let mut tokens = self.0.clone();
tokens.truncate(level);
Self(tokens)
}
}
pub const fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn is_ancestor_of(&self, other: &Self) -> bool {
let me = format!("{}.", self);
other.to_string().as_str().starts_with(me.as_str()) || self.is_empty()
}
pub fn is_ancestor_or_equal_of(&self, other: &Self) -> bool {
let me = format!("{}.", self);
other.to_string().as_str().starts_with(me.as_str())
|| self.is_empty()
|| self == other
}
pub fn is_descendant_of(&self, other: &Self) -> bool {
let me = self.to_string();
me.as_str().starts_with(format!("{}.", other).as_str())
}
pub fn is_parent_of(&self, other: &Self) -> bool {
*self == other.parent()
}
pub fn is_child_of(&self, other: &Self) -> bool {
self.parent() == *other
}
pub const fn is_top_level(&self) -> bool {
self.0.len() == 1
}
}
impl std::fmt::Display for Namespace {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self.level().cmp(&1) {
Ordering::Less => write!(f, ""),
Ordering::Equal => write!(f, "{}", self.0[0]),
Ordering::Greater => write!(f, "{}", self.0.join(".")),
}
}
}
impl std::fmt::Debug for Namespace {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
match self.level().cmp(&1) {
Ordering::Less => {
write!(f, "")
}
Ordering::Equal => write!(f, "{}", self.0[0]),
Ordering::Greater => write!(f, "{}", self.0.join(".")),
}
}
}
impl Default for Namespace {
fn default() -> Self {
Self::new()
}
}
impl From<&str> for Namespace {
fn from(str: &str) -> Self {
let tokens: Vec<String> = str
.split('.')
.filter(|x| !x.trim().is_empty())
.map(|s| s.trim().to_string())
.collect();
Self(tokens)
}
}
impl From<String> for Namespace {
fn from(str: String) -> Self {
Self::from(str.as_str())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_namespace() {
let ns = Namespace::from("a.b.c");
assert_eq!(ns.level(), 3);
assert_eq!(ns.key(), "c");
assert_eq!(ns.root().to_string(), "a");
assert_eq!(ns.parent().to_string(), "a.b");
assert_eq!(ns.at_level(1).to_string(), "a");
assert_eq!(ns.at_level(2).to_string(), "a.b");
assert_eq!(ns.at_level(3).to_string(), "a.b.c");
assert!(!ns.is_empty());
assert!(ns.is_ancestor_of(&Namespace::from("a.b.c.d")));
assert!(ns.is_descendant_of(&Namespace::from("a.b")));
assert!(ns.is_parent_of(&Namespace::from("a.b.c.d")));
assert!(ns.is_child_of(&Namespace::from("a.b")));
assert!(!ns.is_top_level());
assert!(Namespace::new().is_ancestor_of(&Namespace::from("a.b.c.d")));
}
}