use core::convert::Infallible;
use core::fmt;
use core::iter::FusedIterator;
use core::str;
use core::str::FromStr;
use alloc::string::String;
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Field(String);
impl fmt::Debug for Field {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "\"{}\"", self.0)
}
}
impl fmt::Display for Field {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl<A> FromIterator<A> for Field
where
A: AsRef<str>,
{
fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Self {
let mut inner = String::new();
for component in iter {
let component = component.as_ref();
let component = component.trim_matches('.');
push_back_component(&mut inner, component);
}
Self(inner)
}
}
impl FromStr for Field {
type Err = Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(s.split('.').collect())
}
}
impl Field {
#[must_use]
pub const fn empty() -> Self {
Self(String::new())
}
#[must_use]
pub fn len(&self) -> usize {
self.0.chars().filter(|&c| c == '.').count() + 1
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
#[must_use]
pub fn components(&self) -> Components<'_> {
Components::new(self)
}
pub fn push_front(&mut self, component: &str) {
push_front_component(&mut self.0, component);
}
pub fn push_back(&mut self, component: &str) {
push_back_component(&mut self.0, component);
}
}
impl From<Field> for String {
fn from(f: Field) -> Self {
f.0
}
}
fn push_front_component(s: &mut String, component: &str) {
if !s.is_empty() {
s.reserve(component.len() + 1);
s.insert(0, '.');
}
s.insert_str(0, component);
}
fn push_back_component(s: &mut String, component: &str) {
if component.is_empty() {
return;
}
if !s.is_empty() {
s.reserve(1 + component.len());
s.push('.');
}
s.push_str(component);
}
#[derive(Clone)]
pub struct Components<'a>(str::Split<'a, char>);
impl fmt::Debug for Components<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
struct AsList<'a, 'b>(&'a Components<'b>);
impl fmt::Debug for AsList<'_, '_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.0.clone()).finish()
}
}
f.debug_tuple("Components").field(&AsList(self)).finish()
}
}
impl<'a> Components<'a> {
fn new(field: &'a Field) -> Self {
Self(field.0.split('.'))
}
}
impl<'a> Iterator for Components<'a> {
type Item = &'a str;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
match self.0.next() {
Some("") => None,
Some(x) => Some(x),
None => None,
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
impl DoubleEndedIterator for Components<'_> {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
match self.0.next_back() {
Some("") => None,
Some(x) => Some(x),
None => None,
}
}
}
impl FusedIterator for Components<'_> {}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! assert_field_eq {
($field:expr, $components:expr, $str:expr ) => {{
use ::alloc::vec::Vec;
let field: Field = $field;
let components: &[&str] = $components;
let str: &str = $str;
assert_eq!(field.components().collect::<Vec<_>>(), components);
assert_eq!(field.as_str(), str);
let mut reconstructed = Field::empty();
for c in components {
reconstructed.push_back(c);
}
assert_eq!(reconstructed, field);
}};
}
#[test]
fn test_simple() {
assert_field_eq!("".parse().unwrap(), &[], "");
assert_field_eq!("test".parse().unwrap(), &["test"], "test");
assert_field_eq!(
"test.field.name".parse().unwrap(),
&["test", "field", "name"],
"test.field.name"
);
}
#[test]
fn test_empty_component() {
assert_field_eq!(".".parse().unwrap(), &[], "");
assert_field_eq!("...".parse().unwrap(), &[], "");
assert_field_eq!(
"test..name".parse().unwrap(),
&["test", "name"],
"test.name"
);
assert_field_eq!(".test".parse().unwrap(), &["test"], "test");
assert_field_eq!("...test".parse().unwrap(), &["test"], "test");
assert_field_eq!("test.".parse().unwrap(), &["test"], "test");
assert_field_eq!("test...".parse().unwrap(), &["test"], "test");
}
#[test]
fn test_weird_component() {
assert_field_eq!(
" περιεργο κομματι ".parse().unwrap(),
&[" περιεργο κομματι "],
" περιεργο κομματι "
);
assert_field_eq!(
"test. περιεργο κομματι .name".parse().unwrap(),
&["test", " περιεργο κομματι ", "name"],
"test. περιεργο κομματι .name"
);
}
}