use std::fmt;
use std::str::FromStr;
use crate::arguments::Arguments;
use crate::parse::ArgumentParser;
use crate::{CaseFoldMap, Error, ErrorKind, validate};
#[derive(Clone, Debug, Default, PartialEq, Eq)]
struct Attribute {
position: usize,
value: Option<String>,
}
#[derive(Debug, Default, PartialEq, Eq)]
pub struct AttributeList {
attributes: CaseFoldMap<'static, Attribute>,
}
impl Clone for AttributeList {
#[inline]
fn clone(&self) -> Self {
Self {
attributes: self.attributes.clone(),
}
}
#[inline]
fn clone_from(&mut self, source: &Self) {
self.attributes.clone_from(&source.attributes);
}
}
impl AttributeList {
pub fn new() -> Self {
Self::default()
}
pub fn is_empty(&self) -> bool {
self.attributes.is_empty()
}
pub fn len(&self) -> usize {
self.attributes.len()
}
pub fn contains(&self, name: &str) -> bool {
self.attributes.contains_key(name)
}
pub fn get(&self, name: &str) -> Option<&str> {
Some(self.attributes.get(name)?.value.as_ref()?.as_str())
}
pub fn position(&self, name: &str) -> Option<usize> {
Some(self.attributes.get(name)?.position)
}
pub fn push(&mut self, name: String, default_value: Option<String>) {
self.attributes.insert(
name,
Attribute {
position: self.attributes.len(),
value: default_value,
},
);
}
pub(crate) fn truncate(&mut self, i: usize) {
self.attributes.retain(|_, value| value.position < i);
}
pub(crate) fn find<'a>(&'a self, name: &str, args: &Arguments<'a>) -> Option<&'a str> {
if let Some(entity) = args.get(name) {
return Some(entity);
}
let attribute = self.attributes.get(name)?;
if let Some(entity) = args.at(attribute.position) {
return Some(entity);
}
attribute.value.as_deref()
}
pub(crate) fn append(&mut self, source: &str) -> crate::Result<()> {
self.append_args(ArgumentParser::new(source))
}
pub(crate) fn append_args(&mut self, args: ArgumentParser) -> crate::Result<()> {
self.attributes.reserve(args.size_hint().1.unwrap());
for entry in args {
let (name, value) = entry?;
validate(name, ErrorKind::InvalidArgumentName)?;
if self.attributes.contains_key(name) {
return Err(Error::new(name, ErrorKind::DuplicateAttributeInAttlist));
}
self.push(name.to_owned(), value.map(ToOwned::to_owned));
}
Ok(())
}
}
impl fmt::Display for AttributeList {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use crate::display::MaybeQuote;
for i in 0..self.attributes.len() {
if i != 0 {
f.write_str(" ")?;
}
let Some((name, attr)) = self.attributes.iter().find(|(_, v)| v.position == i) else {
continue;
};
match &attr.value {
Some(value) => write!(f, "{name}={}", MaybeQuote(value))?,
None => write!(f, "{name}")?,
}
}
Ok(())
}
}
impl TryFrom<ArgumentParser<'_>> for AttributeList {
type Error = Error;
fn try_from(value: ArgumentParser) -> crate::Result<Self> {
let mut list = AttributeList::new();
list.append_args(value)?;
Ok(list)
}
}
impl FromStr for AttributeList {
type Err = Error;
fn from_str(s: &str) -> crate::Result<Self> {
ArgumentParser::new(s).try_into()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fmt() {
let args = ArgumentParser::new("EL RName FLAG=\"Room Name\" Owner Locked=0");
let formatted = AttributeList::try_from(args).unwrap().to_string();
assert_eq!(formatted, "EL RName FLAG=\"Room Name\" Owner Locked=0");
}
}