use crate::contentline;
use std::borrow::Cow;
use std::collections::BTreeMap;
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Component<'a> {
pub(crate) name: Cow<'a, str>,
pub(crate) properties: Vec<Property<'a>>,
pub(crate) subcomponents: Vec<Component<'a>>,
}
impl<'a> Component<'a> {
pub fn new<S>(name: S) -> Self
where
S: Into<Cow<'a, str>>,
{
Component {
name: name.into(),
properties: Vec::new(),
subcomponents: Vec::new(),
}
}
pub fn add_property<P>(&mut self, property: P)
where
P: Into<Property<'a>>,
{
self.properties.push(property.into());
}
pub fn add_component<C>(&mut self, component: C)
where
C: Into<Component<'a>>,
{
self.subcomponents.push(component.into());
}
}
impl<'a> fmt::Display for Component<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "BEGIN:{}\r", self.name)?;
for property in &self.properties {
write!(f, "{}", property)?;
}
for component in &self.subcomponents {
write!(f, "{}", component)?;
}
writeln!(f, "END:{}\r", self.name)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Property<'a> {
pub(crate) key: Cow<'a, str>,
pub(crate) value: Cow<'a, str>,
pub(crate) parameters: Parameters<'a>,
}
impl<'a> Property<'a> {
pub fn new<K, V>(key: K, value: V) -> Self
where
K: Into<Cow<'a, str>>,
V: Into<Cow<'a, str>>,
{
Property {
key: key.into(),
value: value.into(),
parameters: BTreeMap::new(),
}
}
pub fn add<P>(&mut self, parameter: P)
where
P: Into<Parameter<'a>>,
{
let parameter = parameter.into();
self.parameters.insert(parameter.key, parameter.value);
}
pub fn append(&mut self, mut parameters: Parameters<'a>) {
self.parameters.append(&mut parameters);
}
fn content_len(&self) -> usize {
self.parameters
.iter()
.fold(self.value.len() + self.key.len() + 1, |len, (k, v)| {
len + k.len() + v.len() + 2
})
}
fn format<W: fmt::Write>(&self, writer: &mut W) -> fmt::Result {
write!(writer, "{}", self.key)?;
for (key, value) in &self.parameters {
write!(writer, ";{}={}", key, value)?;
}
write!(writer, ":{}", self.value)
}
}
impl<'a> fmt::Display for Property<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let len = self.content_len();
if len <= contentline::LIMIT {
self.format(f)?;
} else {
let mut content = String::with_capacity(contentline::size(len));
self.format(&mut content)?;
contentline::fold(f, &content)?;
}
writeln!(f, "\r")
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Parameter<'a> {
pub(crate) key: Cow<'a, str>,
pub(crate) value: Cow<'a, str>,
}
impl<'a> Parameter<'a> {
pub fn new<K, V>(key: K, value: V) -> Self
where
K: Into<Cow<'a, str>>,
V: Into<Cow<'a, str>>,
{
Parameter {
key: key.into(),
value: value.into(),
}
}
}
impl<'a> fmt::Display for Parameter<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}={}", self.key, self.value)
}
}
pub type Parameters<'a> = BTreeMap<Cow<'a, str>, Cow<'a, str>>;
#[cfg(test)]
mod tests {
use super::{Parameter, Property};
#[test]
fn simple() {
let property = Property::new("SUMMARY", "Simple");
let expected = 14;
assert_eq!(property.content_len(), expected);
}
#[test]
fn with_parameter() {
let mut property = Property::new("SUMMARY", "Simple");
property.add(Parameter::new("VALUE", "TEXT"));
let expected = 25;
assert_eq!(property.content_len(), expected);
}
}