use std::fmt;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ResponsePath {
path: String,
}
impl ResponsePath {
pub fn new(path: impl Into<String>) -> Self {
Self { path: path.into() }
}
pub fn empty() -> Self {
Self {
path: String::new(),
}
}
pub fn child(&self, name: &str) -> Self {
if name.is_empty() {
self.clone()
} else if self.path.is_empty() {
Self::new(name)
} else {
Self::new(format!("{}.{}", self.path, name))
}
}
pub fn as_str(&self) -> &str {
&self.path
}
pub fn is_empty(&self) -> bool {
self.path.is_empty()
}
pub fn segments(&self) -> impl Iterator<Item = &str> {
self.path.split('.').filter(|s| !s.is_empty())
}
pub fn len(&self) -> usize {
if self.path.is_empty() {
0
} else {
self.path.split('.').count()
}
}
pub fn strip_prefix(&self, prefix: &str) -> Option<Self> {
if self.path == prefix {
Some(Self::empty())
} else if self.path.starts_with(prefix) && self.path[prefix.len()..].starts_with('.') {
Some(Self::new(&self.path[prefix.len() + 1..]))
} else {
None
}
}
pub fn strip_path_prefix(&self, prefix: &ResponsePath) -> Option<Self> {
self.strip_prefix(prefix.as_str())
}
pub fn first(&self) -> Option<&str> {
self.segments().next()
}
pub fn last(&self) -> Option<&str> {
self.path.rsplit('.').next().filter(|s| !s.is_empty())
}
pub fn parent(&self) -> Self {
if let Some(last_dot) = self.path.rfind('.') {
Self::new(&self.path[..last_dot])
} else {
Self::empty()
}
}
}
impl fmt::Display for ResponsePath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.path)
}
}
impl From<&str> for ResponsePath {
fn from(s: &str) -> Self {
Self::new(s)
}
}
impl From<String> for ResponsePath {
fn from(s: String) -> Self {
Self::new(s)
}
}
impl From<&String> for ResponsePath {
fn from(s: &String) -> Self {
Self::new(s.clone())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new() {
let path = ResponsePath::new("name");
assert_eq!(path.as_str(), "name");
}
#[test]
fn child() {
let path = ResponsePath::new("address").child("street");
assert_eq!(path.as_str(), "address.street");
}
#[test]
fn child_from_empty() {
let path = ResponsePath::empty().child("name");
assert_eq!(path.as_str(), "name");
}
#[test]
fn strip_prefix() {
let path = ResponsePath::new("address.street");
let stripped = path.strip_prefix("address").unwrap();
assert_eq!(stripped.as_str(), "street");
assert!(path.strip_prefix("other").is_none());
}
#[test]
fn strip_prefix_exact_match() {
let path = ResponsePath::new("name");
let stripped = path.strip_prefix("name").unwrap();
assert!(stripped.is_empty());
}
#[test]
fn segments() {
let path = ResponsePath::new("address.location.city");
let segments: Vec<_> = path.segments().collect();
assert_eq!(segments, vec!["address", "location", "city"]);
}
#[test]
fn display() {
let path = ResponsePath::new("address.street");
assert_eq!(format!("{}", path), "address.street");
}
#[test]
fn from_str() {
let path: ResponsePath = "address.street".into();
assert_eq!(path.as_str(), "address.street");
}
}