use core::{
fmt::{self, Write},
mem,
str::FromStr,
};
use super::{Name, PathParseError};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Element {
Name(Name),
IndexedField(IndexedField),
}
impl Element {
pub fn new_name<T>(name: T) -> Self
where
T: Into<Name>,
{
Self::Name(name.into())
}
pub fn new_indexed_field<N, I>(name: N, indexes: I) -> Self
where
N: Into<Name>,
I: Indexes,
{
let indexes = indexes.into_indexes();
if indexes.is_empty() {
Self::new_name(name)
} else {
Self::IndexedField(IndexedField {
name: name.into(),
indexes,
})
}
}
}
impl fmt::Display for Element {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Element::Name(name) => name.fmt(f),
Element::IndexedField(field_index) => field_index.fmt(f),
}
}
}
impl From<Element> for String {
fn from(element: Element) -> Self {
match element {
Element::Name(name) => name.into(),
Element::IndexedField(new_indexed_field) => new_indexed_field.to_string(),
}
}
}
impl From<IndexedField> for Element {
fn from(value: IndexedField) -> Self {
if value.indexes.is_empty() {
Self::Name(value.name)
} else {
Self::IndexedField(value)
}
}
}
impl<N, I> From<(N, I)> for Element
where
N: Into<Name>,
I: Indexes,
{
fn from((name, indexes): (N, I)) -> Self {
Self::new_indexed_field(name, indexes)
}
}
impl From<Name> for Element {
fn from(name: Name) -> Self {
Self::Name(name)
}
}
impl FromStr for Element {
type Err = PathParseError;
fn from_str(input: &str) -> Result<Self, Self::Err> {
let mut remaining = input;
let mut name = None;
let mut indexes = Vec::new();
while !remaining.is_empty() {
let open = remaining.find('[');
let close = remaining.find(']');
match (open, close) {
(None, None) => {
if name.is_some() {
return Err(PathParseError);
}
name = Some(mem::take(&mut remaining));
break;
}
(None, Some(_close)) => return Err(PathParseError),
(Some(_open), None) => return Err(PathParseError),
(Some(open), Some(close)) => {
if open >= close {
return Err(PathParseError);
}
if name.is_none() {
if open > 0 {
name = Some(&remaining[..open]);
} else {
return Err(PathParseError);
}
} else if open > 0 {
return Err(PathParseError);
}
let index: usize = remaining[open + 1..close]
.parse()
.map_err(|_| PathParseError)?;
indexes.push(index);
remaining = &remaining[close + 1..];
}
}
}
Ok(if indexes.is_empty() {
Self::Name(input.into())
} else {
if !remaining.is_empty() {
return Err(PathParseError);
}
let name = name.ok_or(PathParseError)?;
indexes.shrink_to_fit();
Self::IndexedField(IndexedField {
name: name.into(),
indexes,
})
})
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct IndexedField {
pub(crate) name: Name,
indexes: Vec<usize>,
}
impl fmt::Display for IndexedField {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.name.fmt(f)?;
self.indexes.iter().try_for_each(|index| {
f.write_char('[')?;
index.fmt(f)?;
f.write_char(']')
})
}
}
pub trait Indexes {
fn into_indexes(self) -> Vec<usize>;
}
impl Indexes for usize {
fn into_indexes(self) -> Vec<usize> {
vec![self]
}
}
impl Indexes for Vec<usize> {
fn into_indexes(self) -> Vec<usize> {
self
}
}
impl Indexes for &[usize] {
fn into_indexes(self) -> Vec<usize> {
self.to_vec()
}
}
impl<const N: usize> Indexes for [usize; N] {
fn into_indexes(self) -> Vec<usize> {
self.to_vec()
}
}
impl<const N: usize> Indexes for &[usize; N] {
fn into_indexes(self) -> Vec<usize> {
self.to_vec()
}
}
#[cfg(test)]
mod test {
use pretty_assertions::assert_eq;
use crate::{Num, Path};
use super::{Element, Name};
#[test]
fn display_name() {
let path = Element::new_name("foo");
assert_eq!("foo", path.to_string());
}
#[test]
fn display_indexed() {
let path = Element::new_indexed_field("foo", 42);
assert_eq!("foo[42]", path.to_string());
let path = Element::new_indexed_field("foo", [42]);
assert_eq!("foo[42]", path.to_string());
let path = Element::new_indexed_field("foo", &([42, 37, 9])[..]);
assert_eq!("foo[42][37][9]", path.to_string());
}
#[test]
fn display_path() {
let path: Path = ["foo", "bar"].into_iter().map(Name::from).collect();
assert_eq!("foo.bar", path.to_string());
let path = Path::from_iter([
Element::new_name("foo"),
Element::new_indexed_field("bar", 42),
]);
assert_eq!("foo.bar[42]", path.to_string());
let path = Path::from_iter([
Element::new_indexed_field("foo", 42),
Element::new_name("bar"),
]);
assert_eq!("foo[42].bar", path.to_string());
}
#[test]
fn size() {
assert_eq!(
"size(a) = 0",
"a".parse::<Path>()
.unwrap()
.size()
.equal(Num::new(0))
.to_string()
);
}
}