ics/components.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
//! Basic components for building custom calendar objects.
//!
//! To create new custom (IANA/non-standard) calendar
//! components/properties/parameters, the [Display](https://doc.rust-lang.org/std/fmt/trait.Display.html) implementation of the base
//! components should be used to avoid conflicting formatting.
//!
//! # Example
//! ```
//! // Implementing Display for new component
//! use ics::components::Component;
//! use std::fmt;
//!
//! pub struct MyCustomComponent<'a>(Component<'a>);
//!
//! impl<'a> fmt::Display for MyCustomComponent<'a> {
//! fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
//! write!(f, "{}", self.0)
//! }
//! }
//! ```
use crate::contentline;
use std::borrow::Cow;
use std::collections::BTreeMap;
use std::fmt;
/// A `Component` contains properties and sometimes sub-components.
///
/// This can be used to create a new calendar component by either creating a
/// wrapper type or just use it as it is.
#[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> {
/// Creates a new component with the given name.
pub fn new<S>(name: S) -> Self
where
S: Into<Cow<'a, str>>,
{
Component {
name: name.into(),
properties: Vec::new(),
subcomponents: Vec::new(),
}
}
/// Adds a property to a component. Some properties can be added multiple
/// times. Each occurrence will be shown as single content line.
pub fn add_property<P>(&mut self, property: P)
where
P: Into<Property<'a>>,
{
self.properties.push(property.into());
}
/// Adds a sub-component to this component.
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)
}
}
/// A `Property` contains a key-value pair which can have optionally several
/// parameters.
///
/// They are part of a component and define it. This can be used to create a
/// new calendar property by either creating a wrapper type or just use it as
/// it is.
#[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> {
/// Creates a new property with the given key and value.
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(),
}
}
/// Adds a parameter to a property.
pub fn add<P>(&mut self, parameter: P)
where
P: Into<Parameter<'a>>,
{
let parameter = parameter.into();
self.parameters.insert(parameter.key, parameter.value);
}
/// Adds several parameters at once to a property. For creating several
/// parameters at once, consult the documentation of the `parameters!`
/// macro.
pub fn append(&mut self, mut parameters: Parameters<'a>) {
self.parameters.append(&mut parameters);
}
fn content_len(&self) -> usize {
// + 1 for the : in the property
// + 2 for the ; and = in the parameter
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")
}
}
/// A `Parameter` is a key-value that can be added to a property to specify it
/// more.
///
/// This can be used to create a new calendar parameter by either creating a
/// wrapper type or just use it as it is.
#[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> {
/// Creates a new property with the given key and value.
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)
}
}
/// `Parameters` is a collection of `Parameter`s. It can be created with the
/// `parameters!` macro.
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);
}
}