use std::fmt;
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct RustdvPath {
segments: Vec<String>,
rendered: String,
}
impl RustdvPath {
pub fn root(name: &str) -> RustdvPath {
RustdvPath { segments: vec![name.to_string()], rendered: name.to_string() }
}
pub fn empty() -> RustdvPath {
RustdvPath::default()
}
pub fn child(&self, name: &str) -> RustdvPath {
let mut segments = self.segments.clone();
segments.push(name.to_string());
let rendered =
if self.rendered.is_empty() { name.to_string() } else { format!("{}.{}", self.rendered, name) };
RustdvPath { segments, rendered }
}
pub fn as_str(&self) -> &str {
&self.rendered
}
pub fn segments(&self) -> &[String] {
&self.segments
}
pub fn depth(&self) -> usize {
self.segments.len()
}
pub fn is_empty(&self) -> bool {
self.segments.is_empty()
}
pub fn is_under(&self, prefix: &RustdvPath) -> bool {
prefix.segments.len() <= self.segments.len()
&& self.segments[..prefix.segments.len()] == prefix.segments[..]
}
}
impl fmt::Display for RustdvPath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.rendered)
}
}
pub fn str_is_under(path: &str, prefix: &str) -> bool {
if prefix.is_empty() {
return true;
}
let mut have = path.split('.');
for want in prefix.split('.') {
match have.next() {
Some(seg) if seg == want => continue,
_ => return false,
}
}
true
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn child_extends_and_renders() {
let root = RustdvPath::root("RandomTest");
let env = root.child("env");
let sb = env.child("scoreboard");
assert_eq!(sb.as_str(), "RandomTest.env.scoreboard");
assert_eq!(sb.depth(), 3);
assert_eq!(sb.segments(), ["RandomTest", "env", "scoreboard"]);
}
#[test]
fn under_is_segment_wise() {
let a = RustdvPath::root("a");
let ab = a.child("b");
assert!(ab.is_under(&a));
assert!(a.is_under(&a));
assert!(!a.is_under(&ab));
assert!(!RustdvPath::root("ab").is_under(&a));
}
#[test]
fn str_under_matches_on_segments() {
assert!(str_is_under("a.b", "a"));
assert!(str_is_under("a", "a"));
assert!(!str_is_under("ab", "a")); assert!(!str_is_under("a", "a.b"));
assert!(str_is_under("anything", ""));
}
#[test]
fn empty_path_has_no_segments() {
let e = RustdvPath::empty();
assert!(e.is_empty());
assert_eq!(e.depth(), 0);
assert_eq!(e.as_str(), "");
assert_eq!(e.child("top").as_str(), "top");
}
}