pub mod binary;
pub mod boolean;
pub mod cal_address;
pub mod datetime;
pub mod duration;
pub mod float;
pub mod geo;
pub mod integer;
pub mod period;
pub mod recur;
pub mod request_status;
pub mod text;
pub mod uri;
pub mod utc_offset;
use core::{error, fmt, ops, str};
use alloc::{
borrow::Cow,
string::{String, ToString},
vec::Vec,
};
use crate::value::{
binary::IcalBinary,
boolean::IcalBoolean,
cal_address::IcalCalAddress,
datetime::{IcalDate, IcalDateTime, IcalDateTimeList, IcalTime},
duration::IcalDuration,
float::IcalFloat,
geo::IcalGeo,
integer::IcalInteger,
period::IcalPeriod,
recur::IcalRecur,
request_status::IcalRequestStatus,
text::{IcalText, IcalTextList},
uri::IcalUri,
utc_offset::IcalUtcOffset,
};
#[derive(Debug)]
pub struct ParseIcalValueKindError(
String,
);
impl fmt::Display for ParseIcalValueKindError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Cannot parse iCalendar value type `{}`", self.0)
}
}
impl error::Error for ParseIcalValueKindError {}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum IcalValueKind {
Binary,
Boolean,
CalAddress,
Date,
DateTime,
DateTimeList,
Duration,
Float,
Geo,
Integer,
Period,
Recur,
RequestStatus,
Text,
TextList,
Time,
Uri,
UtcOffset,
}
impl IcalValueKind {
pub const ALL: [Self; 18] = [
Self::Binary,
Self::Boolean,
Self::CalAddress,
Self::Date,
Self::DateTime,
Self::DateTimeList,
Self::Duration,
Self::Float,
Self::Geo,
Self::Integer,
Self::Period,
Self::Recur,
Self::RequestStatus,
Self::Text,
Self::TextList,
Self::Time,
Self::Uri,
Self::UtcOffset,
];
}
impl str::FromStr for IcalValueKind {
type Err = ParseIcalValueKindError;
fn from_str(kind: &str) -> Result<Self, Self::Err> {
match kind {
kind if kind.eq_ignore_ascii_case("BINARY") => Ok(Self::Binary),
kind if kind.eq_ignore_ascii_case("BOOLEAN") => Ok(Self::Boolean),
kind if kind.eq_ignore_ascii_case("CAL-ADDRESS") => Ok(Self::CalAddress),
kind if kind.eq_ignore_ascii_case("DATE") => Ok(Self::Date),
kind if kind.eq_ignore_ascii_case("DATE-TIME") => Ok(Self::DateTime),
kind if kind.eq_ignore_ascii_case("DATE-TIME-LIST") => Ok(Self::DateTimeList),
kind if kind.eq_ignore_ascii_case("DURATION") => Ok(Self::Duration),
kind if kind.eq_ignore_ascii_case("FLOAT") => Ok(Self::Float),
kind if kind.eq_ignore_ascii_case("GEO") => Ok(Self::Geo),
kind if kind.eq_ignore_ascii_case("INTEGER") => Ok(Self::Integer),
kind if kind.eq_ignore_ascii_case("PERIOD") => Ok(Self::Period),
kind if kind.eq_ignore_ascii_case("RECUR") => Ok(Self::Recur),
kind if kind.eq_ignore_ascii_case("REQUEST-STATUS") => Ok(Self::RequestStatus),
kind if kind.eq_ignore_ascii_case("TEXT") => Ok(Self::Text),
kind if kind.eq_ignore_ascii_case("TEXT-LIST") => Ok(Self::TextList),
kind if kind.eq_ignore_ascii_case("TIME") => Ok(Self::Time),
kind if kind.eq_ignore_ascii_case("URI") => Ok(Self::Uri),
kind if kind.eq_ignore_ascii_case("UTC-OFFSET") => Ok(Self::UtcOffset),
_ => Err(ParseIcalValueKindError(kind.to_string())),
}
}
}
impl ops::Deref for IcalValueKind {
type Target = str;
fn deref(&self) -> &Self::Target {
match self {
Self::Binary => "BINARY",
Self::Boolean => "BOOLEAN",
Self::CalAddress => "CAL-ADDRESS",
Self::Date => "DATE",
Self::DateTime => "DATE-TIME",
Self::DateTimeList => "DATE-TIME-LIST",
Self::Duration => "DURATION",
Self::Float => "FLOAT",
Self::Geo => "GEO",
Self::Integer => "INTEGER",
Self::Period => "PERIOD",
Self::Recur => "RECUR",
Self::RequestStatus => "REQUEST-STATUS",
Self::Text => "TEXT",
Self::TextList => "TEXT-LIST",
Self::Time => "TIME",
Self::Uri => "URI",
Self::UtcOffset => "UTC-OFFSET",
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum IcalValue<'a> {
Binary(IcalBinary<'a>),
Boolean(IcalBoolean<'a>),
CalAddress(IcalCalAddress<'a>),
Date(IcalDate<'a>),
DateTime(IcalDateTime<'a>),
DateTimeList(IcalDateTimeList<'a>),
Duration(IcalDuration<'a>),
Float(IcalFloat<'a>),
Geo(IcalGeo<'a>),
Integer(IcalInteger<'a>),
Period(IcalPeriod<'a>),
Recur(IcalRecur<'a>),
RequestStatus(IcalRequestStatus<'a>),
Text(IcalText<'a>),
TextList(IcalTextList<'a>),
Time(IcalTime<'a>),
Uri(IcalUri<'a>),
UtcOffset(IcalUtcOffset<'a>),
Unknown(IcalUnknownValue<'a>),
}
impl IcalValue<'_> {
pub fn kind(&self) -> Option<IcalValueKind> {
match self {
Self::Binary(_) => Some(IcalValueKind::Binary),
Self::Boolean(_) => Some(IcalValueKind::Boolean),
Self::CalAddress(_) => Some(IcalValueKind::CalAddress),
Self::Date(_) => Some(IcalValueKind::Date),
Self::DateTime(_) => Some(IcalValueKind::DateTime),
Self::DateTimeList(_) => Some(IcalValueKind::DateTimeList),
Self::Duration(_) => Some(IcalValueKind::Duration),
Self::Float(_) => Some(IcalValueKind::Float),
Self::Geo(_) => Some(IcalValueKind::Geo),
Self::Integer(_) => Some(IcalValueKind::Integer),
Self::Period(_) => Some(IcalValueKind::Period),
Self::Recur(_) => Some(IcalValueKind::Recur),
Self::RequestStatus(_) => Some(IcalValueKind::RequestStatus),
Self::Text(_) => Some(IcalValueKind::Text),
Self::TextList(_) => Some(IcalValueKind::TextList),
Self::Time(_) => Some(IcalValueKind::Time),
Self::Uri(_) => Some(IcalValueKind::Uri),
Self::UtcOffset(_) => Some(IcalValueKind::UtcOffset),
Self::Unknown(_) => None,
}
}
pub fn into_owned(self) -> IcalValue<'static> {
match self {
Self::Binary(IcalBinary::Uri(value)) => {
IcalValue::Binary(IcalBinary::Uri(owned(value)))
}
Self::Binary(IcalBinary::Base64(value)) => {
IcalValue::Binary(IcalBinary::Base64(owned(value)))
}
Self::Boolean(value) => IcalValue::Boolean(IcalBoolean(owned(value.0))),
Self::CalAddress(value) => IcalValue::CalAddress(IcalCalAddress(owned(value.0))),
Self::Date(value) => IcalValue::Date(IcalDate(owned(value.0))),
Self::DateTime(value) => IcalValue::DateTime(IcalDateTime(owned(value.0))),
Self::DateTimeList(value) => {
IcalValue::DateTimeList(IcalDateTimeList(value.0.into_iter().map(owned).collect()))
}
Self::Duration(value) => IcalValue::Duration(IcalDuration(owned(value.0))),
Self::Float(value) => IcalValue::Float(IcalFloat(owned(value.0))),
Self::Geo(value) => IcalValue::Geo(IcalGeo {
latitude: owned(value.latitude),
longitude: owned(value.longitude),
}),
Self::Integer(value) => IcalValue::Integer(IcalInteger(owned(value.0))),
Self::Period(value) => IcalValue::Period(IcalPeriod(owned(value.0))),
Self::Recur(value) => IcalValue::Recur(IcalRecur(owned(value.0))),
Self::RequestStatus(value) => IcalValue::RequestStatus(IcalRequestStatus {
code: owned(value.code),
description: owned(value.description),
extra: owned(value.extra),
}),
Self::Text(value) => IcalValue::Text(IcalText(owned(value.0))),
Self::TextList(value) => {
IcalValue::TextList(IcalTextList(value.0.into_iter().map(owned).collect()))
}
Self::Time(value) => IcalValue::Time(IcalTime(owned(value.0))),
Self::Uri(value) => IcalValue::Uri(IcalUri(owned(value.0))),
Self::UtcOffset(value) => IcalValue::UtcOffset(IcalUtcOffset(owned(value.0))),
Self::Unknown(value) => IcalValue::Unknown(IcalUnknownValue {
components: value
.components
.into_iter()
.map(|component| component.into_iter().map(owned).collect())
.collect(),
}),
}
}
}
pub(crate) fn owned(text: Cow<'_, str>) -> Cow<'static, str> {
Cow::Owned(text.into_owned())
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct IcalUnknownValue<'a> {
pub components: Vec<Vec<Cow<'a, str>>>,
}
#[cfg(test)]
mod tests {
use core::str::FromStr;
use crate::value::{IcalUnknownValue, IcalValue, IcalValueKind, text::IcalText};
#[test]
fn reports_the_kind_of_a_value_and_none_for_unknown() {
assert_eq!(
IcalValue::Text(IcalText::default()).kind(),
Some(IcalValueKind::Text),
);
assert_eq!(IcalValue::Unknown(IcalUnknownValue::default()).kind(), None);
}
#[test]
fn maps_value_param_strings_liberally_and_case_insensitively() {
assert_eq!("URI".parse().ok(), Some(IcalValueKind::Uri));
assert_eq!("date-time".parse().ok(), Some(IcalValueKind::DateTime));
assert!(IcalValueKind::from_str("bogus").is_err());
}
}