use serde::de::Error;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::ops::Deref;
use std::str::FromStr;
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct Items(Vec<Item>);
impl Items {
pub fn as_slice(&self) -> &[Item] {
self.0.as_slice()
}
pub fn new(items: Vec<Item>) -> Self {
Self(items)
}
pub fn into_inner(self) -> Vec<Item> {
self.0
}
}
impl Deref for Items {
type Target = [Item];
fn deref(&self) -> &Self::Target {
self.0.as_slice()
}
}
impl<'a> IntoIterator for &'a Items {
type Item = &'a Item;
type IntoIter = std::slice::Iter<'a, Item>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
impl IntoIterator for Items {
type Item = Item;
type IntoIter = std::vec::IntoIter<Item>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl FromIterator<Item> for Items {
fn from_iter<T: IntoIterator<Item = Item>>(iter: T) -> Self {
Self(iter.into_iter().collect())
}
}
impl FromStr for Items {
type Err = UnclosedQuotesError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut items = Vec::new();
let mut in_double_quotes = false;
let mut item_start_at = 0;
let mut chars = s.chars().enumerate().peekable();
while let Some((index, char)) = chars.next() {
if in_double_quotes {
if char == '"' {
let item = String::from(&s[item_start_at..index]);
items.push(Item::try_from(item).unwrap());
item_start_at = index + 1;
in_double_quotes = false;
} else {
continue;
}
} else if char == '"' {
in_double_quotes = true;
item_start_at = index + 1;
} else if let Some(end) = Some(index)
.filter(|_| char.is_ascii_whitespace())
.or_else(|| Some(index + 1).filter(|_| chars.peek().is_none()))
{
let item = String::from(s[item_start_at..end].trim());
if !item.is_empty() {
items.push(Item::try_from(item).unwrap());
}
item_start_at = index + 1;
}
}
if in_double_quotes {
Err(UnclosedQuotesError)
} else {
Ok(Self(items))
}
}
}
impl std::fmt::Display for Items {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
for (index, item) in self.iter().enumerate() {
let space = if index == 0 { "" } else { " " };
if item.contains(' ') {
write!(f, "{}\"{}\"", space, item)?;
} else {
write!(f, "{}{}", space, item)?;
}
}
Ok(())
}
}
impl<S: Into<String>> TryFrom<Vec<S>> for Items {
type Error = InvalidItemError;
fn try_from(value: Vec<S>) -> Result<Self, Self::Error> {
value
.into_iter()
.map(|s| s.into())
.map(Item::try_from)
.collect()
}
}
impl Serialize for Items {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl<'de> Deserialize<'de> for Items {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let str = <std::borrow::Cow<str>>::deserialize(deserializer)?;
str.parse().map_err(D::Error::custom)
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Item(String);
impl Item {
pub fn into_inner(self) -> String {
self.0
}
}
impl std::fmt::Display for Item {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.write_str(&self.0)
}
}
impl Deref for Item {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl AsRef<str> for Item {
fn as_ref(&self) -> &str {
self.0.as_ref()
}
}
impl TryFrom<String> for Item {
type Error = InvalidItemError;
fn try_from(value: String) -> Result<Self, Self::Error> {
if value.contains('"') {
Err(InvalidItemError)
} else {
Ok(Self(value))
}
}
}
impl TryFrom<&str> for Item {
type Error = InvalidItemError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
String::from(value).try_into()
}
}
impl FromStr for Item {
type Err = InvalidItemError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.try_into()
}
}
impl From<Item> for String {
fn from(i: Item) -> Self {
i.0
}
}
impl PartialEq<&str> for Item {
fn eq(&self, other: &&str) -> bool {
self.0 == *other
}
}
impl PartialEq<Item> for &str {
fn eq(&self, other: &Item) -> bool {
*self == other.0
}
}
impl Serialize for Item {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.0)
}
}
impl<'de> Deserialize<'de> for Item {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let str = <std::borrow::Cow<str>>::deserialize(deserializer)?;
str.as_ref().try_into().map_err(D::Error::custom)
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct UnclosedQuotesError;
impl std::fmt::Display for UnclosedQuotesError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.write_str("invalid items: contained an unclosed set of double quotes")
}
}
impl std::error::Error for UnclosedQuotesError {}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct InvalidItemError;
impl std::fmt::Display for InvalidItemError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.write_str("invalid item: items must not contain double quotes")
}
}
impl std::error::Error for InvalidItemError {}