use super::escape;
use std::collections::HashMap;
use std::fmt;
use std::fmt::Write;
use std::str::FromStr;
pub mod error;
pub use error::*;
use lazy_static::lazy_static;
#[derive(Clone, Default, Debug, Eq, PartialEq)]
pub struct Component {
pub(crate) group: Option<String>,
pub name: String,
pub parameters: HashMap<String, String>,
pub values: Vec<Vec<String>>,
}
impl Component {
#[must_use]
pub fn new(name: String) -> Self {
Self {
name,
..Self::default()
}
}
pub fn set_group(&mut self, group: Option<String>) -> bool {
if let Some(ref group) = group {
for (_, c) in group.char_indices() {
if !(c.is_alphanumeric() || c == '-') {
return false;
}
}
}
if let Some(group) = group {
if group.is_empty() {
self.group = None;
} else {
self.group = Some(group);
}
} else {
self.group = group;
}
true
}
#[must_use]
pub const fn get_group(&self) -> &Option<String> {
&self.group
}
}
impl fmt::Display for Component {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut data = String::new();
if let Some(group) = &self.group {
write!(data, "{}.", group)?;
}
write!(data, "{}", self.name)?;
data.push(';');
for (key, val) in &self.parameters {
write!(data, "{}=\"{}\";", key.as_str(), val.as_str())?;
}
data.pop();
data.push(':');
for (i, val) in self.values.iter().enumerate() {
for subval in val {
write!(data, "{},", escape::escape_property(subval.as_str()))?;
}
if !val.is_empty() {
data.pop();
}
if i != self.values.len() - 1 {
data.push(';');
}
}
data = escape::fold_line(data);
data.push_str("\r\n");
write!(f, "{}", data)
}
}
impl FromStr for Component {
type Err = ComponentParseError;
fn from_str(string: &str) -> Result<Self, Self::Err> {
use regex::Regex;
let string = escape::unfold_line(string);
lazy_static! {
static ref REGEX: Regex = Regex::new(r#"^((?P<group>[a-zA-Z0-9-]+)\.)?(?P<name>[a-zA-Z0-9-]+)[:;]?(?P<params>([a-zA-Z0-9-]+=(".+?"|[^"]+?);?)*?):(?P<values>.*)"#).unwrap();
}
let caps = match REGEX.captures(&string) {
Some(caps) => caps,
None => return Err(ComponentParseError::Invalid(string.clone())),
};
return Ok(Self {
group: caps.name("group").map(|x| x.as_str().to_string()),
name: match caps.name("name") {
Some(x) => x.as_str().to_string().to_uppercase(),
None => return Err(ComponentParseError::NoName),
},
parameters: caps
.name("params")
.map(|x| escape::get_parameters(x.as_str().to_string()))
.unwrap_or_default()
.into_iter()
.map(|(k, v)| (k.to_uppercase(), v))
.collect(),
values: match caps.name("values") {
Some(x) => escape::get_values(x.as_str().to_string()),
None => return Err(ComponentParseError::NoValue),
},
});
}
}