use core::{error, fmt, ops, str};
use alloc::{
borrow::Cow,
string::{String, ToString},
vec::Vec,
};
use crate::value::owned;
#[derive(Debug)]
pub struct ParseIcalParamKindError(
String,
);
impl fmt::Display for ParseIcalParamKindError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Cannot parse iCalendar parameter `{}`", self.0)
}
}
impl error::Error for ParseIcalParamKindError {}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum IcalParamKind {
AltRep,
Cn,
CuType,
DelegatedFrom,
DelegatedTo,
Dir,
Encoding,
FmtType,
FbType,
Language,
Member,
PartStat,
Range,
Related,
RelType,
Role,
Rsvp,
SentBy,
TzId,
Value,
Display,
Email,
Feature,
Label,
Order,
Schema,
Derived,
ScheduleAgent,
ScheduleForceSend,
ScheduleStatus,
LinkRel,
Gap,
Charset,
}
impl IcalParamKind {
pub const ALL: [Self; 33] = [
Self::AltRep,
Self::Cn,
Self::CuType,
Self::DelegatedFrom,
Self::DelegatedTo,
Self::Dir,
Self::Encoding,
Self::FmtType,
Self::FbType,
Self::Language,
Self::Member,
Self::PartStat,
Self::Range,
Self::Related,
Self::RelType,
Self::Role,
Self::Rsvp,
Self::SentBy,
Self::TzId,
Self::Value,
Self::Display,
Self::Email,
Self::Feature,
Self::Label,
Self::Order,
Self::Schema,
Self::Derived,
Self::ScheduleAgent,
Self::ScheduleForceSend,
Self::ScheduleStatus,
Self::LinkRel,
Self::Gap,
Self::Charset,
];
}
impl str::FromStr for IcalParamKind {
type Err = ParseIcalParamKindError;
fn from_str(kind: &str) -> Result<Self, Self::Err> {
let kind = match kind {
kind if kind.eq_ignore_ascii_case("ALTREP") => Self::AltRep,
kind if kind.eq_ignore_ascii_case("CN") => Self::Cn,
kind if kind.eq_ignore_ascii_case("CUTYPE") => Self::CuType,
kind if kind.eq_ignore_ascii_case("DELEGATED-FROM") => Self::DelegatedFrom,
kind if kind.eq_ignore_ascii_case("DELEGATED-TO") => Self::DelegatedTo,
kind if kind.eq_ignore_ascii_case("DIR") => Self::Dir,
kind if kind.eq_ignore_ascii_case("ENCODING") => Self::Encoding,
kind if kind.eq_ignore_ascii_case("FMTTYPE") => Self::FmtType,
kind if kind.eq_ignore_ascii_case("FBTYPE") => Self::FbType,
kind if kind.eq_ignore_ascii_case("LANGUAGE") => Self::Language,
kind if kind.eq_ignore_ascii_case("MEMBER") => Self::Member,
kind if kind.eq_ignore_ascii_case("PARTSTAT") => Self::PartStat,
kind if kind.eq_ignore_ascii_case("RANGE") => Self::Range,
kind if kind.eq_ignore_ascii_case("RELATED") => Self::Related,
kind if kind.eq_ignore_ascii_case("RELTYPE") => Self::RelType,
kind if kind.eq_ignore_ascii_case("ROLE") => Self::Role,
kind if kind.eq_ignore_ascii_case("RSVP") => Self::Rsvp,
kind if kind.eq_ignore_ascii_case("SENT-BY") => Self::SentBy,
kind if kind.eq_ignore_ascii_case("TZID") => Self::TzId,
kind if kind.eq_ignore_ascii_case("VALUE") => Self::Value,
kind if kind.eq_ignore_ascii_case("DISPLAY") => Self::Display,
kind if kind.eq_ignore_ascii_case("EMAIL") => Self::Email,
kind if kind.eq_ignore_ascii_case("FEATURE") => Self::Feature,
kind if kind.eq_ignore_ascii_case("LABEL") => Self::Label,
kind if kind.eq_ignore_ascii_case("ORDER") => Self::Order,
kind if kind.eq_ignore_ascii_case("SCHEMA") => Self::Schema,
kind if kind.eq_ignore_ascii_case("DERIVED") => Self::Derived,
kind if kind.eq_ignore_ascii_case("SCHEDULE-AGENT") => Self::ScheduleAgent,
kind if kind.eq_ignore_ascii_case("SCHEDULE-FORCE-SEND") => Self::ScheduleForceSend,
kind if kind.eq_ignore_ascii_case("SCHEDULE-STATUS") => Self::ScheduleStatus,
kind if kind.eq_ignore_ascii_case("LINKREL") => Self::LinkRel,
kind if kind.eq_ignore_ascii_case("GAP") => Self::Gap,
kind if kind.eq_ignore_ascii_case("CHARSET") => Self::Charset,
_ => return Err(ParseIcalParamKindError(kind.to_string())),
};
Ok(kind)
}
}
impl ops::Deref for IcalParamKind {
type Target = str;
fn deref(&self) -> &Self::Target {
match self {
Self::AltRep => "ALTREP",
Self::Cn => "CN",
Self::CuType => "CUTYPE",
Self::DelegatedFrom => "DELEGATED-FROM",
Self::DelegatedTo => "DELEGATED-TO",
Self::Dir => "DIR",
Self::Encoding => "ENCODING",
Self::FmtType => "FMTTYPE",
Self::FbType => "FBTYPE",
Self::Language => "LANGUAGE",
Self::Member => "MEMBER",
Self::PartStat => "PARTSTAT",
Self::Range => "RANGE",
Self::Related => "RELATED",
Self::RelType => "RELTYPE",
Self::Role => "ROLE",
Self::Rsvp => "RSVP",
Self::SentBy => "SENT-BY",
Self::TzId => "TZID",
Self::Value => "VALUE",
Self::Display => "DISPLAY",
Self::Email => "EMAIL",
Self::Feature => "FEATURE",
Self::Label => "LABEL",
Self::Order => "ORDER",
Self::Schema => "SCHEMA",
Self::Derived => "DERIVED",
Self::ScheduleAgent => "SCHEDULE-AGENT",
Self::ScheduleForceSend => "SCHEDULE-FORCE-SEND",
Self::ScheduleStatus => "SCHEDULE-STATUS",
Self::LinkRel => "LINKREL",
Self::Gap => "GAP",
Self::Charset => "CHARSET",
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum IcalParam<'a> {
AltRep(Cow<'a, str>),
Cn(Cow<'a, str>),
CuType(Cow<'a, str>),
DelegatedFrom(Vec<Cow<'a, str>>),
DelegatedTo(Vec<Cow<'a, str>>),
Dir(Cow<'a, str>),
Encoding(Cow<'a, str>),
FmtType(Cow<'a, str>),
FbType(Cow<'a, str>),
Language(Cow<'a, str>),
Member(Vec<Cow<'a, str>>),
PartStat(Cow<'a, str>),
Range(Cow<'a, str>),
Related(Cow<'a, str>),
RelType(Cow<'a, str>),
Role(Cow<'a, str>),
Rsvp(Cow<'a, str>),
SentBy(Cow<'a, str>),
TzId(Cow<'a, str>),
Value(Cow<'a, str>),
Display(Cow<'a, str>),
Email(Cow<'a, str>),
Feature(Vec<Cow<'a, str>>),
Label(Cow<'a, str>),
Order(Cow<'a, str>),
Schema(Cow<'a, str>),
Derived(Cow<'a, str>),
ScheduleAgent(Cow<'a, str>),
ScheduleForceSend(Cow<'a, str>),
ScheduleStatus(Cow<'a, str>),
LinkRel(Cow<'a, str>),
Gap(Cow<'a, str>),
Charset(Cow<'a, str>),
Unknown {
name: Cow<'a, str>,
values: Vec<Cow<'a, str>>,
},
}
impl IcalParam<'_> {
pub fn kind(&self) -> Option<IcalParamKind> {
match self {
Self::AltRep(_) => Some(IcalParamKind::AltRep),
Self::Cn(_) => Some(IcalParamKind::Cn),
Self::CuType(_) => Some(IcalParamKind::CuType),
Self::DelegatedFrom(_) => Some(IcalParamKind::DelegatedFrom),
Self::DelegatedTo(_) => Some(IcalParamKind::DelegatedTo),
Self::Dir(_) => Some(IcalParamKind::Dir),
Self::Encoding(_) => Some(IcalParamKind::Encoding),
Self::FmtType(_) => Some(IcalParamKind::FmtType),
Self::FbType(_) => Some(IcalParamKind::FbType),
Self::Language(_) => Some(IcalParamKind::Language),
Self::Member(_) => Some(IcalParamKind::Member),
Self::PartStat(_) => Some(IcalParamKind::PartStat),
Self::Range(_) => Some(IcalParamKind::Range),
Self::Related(_) => Some(IcalParamKind::Related),
Self::RelType(_) => Some(IcalParamKind::RelType),
Self::Role(_) => Some(IcalParamKind::Role),
Self::Rsvp(_) => Some(IcalParamKind::Rsvp),
Self::SentBy(_) => Some(IcalParamKind::SentBy),
Self::TzId(_) => Some(IcalParamKind::TzId),
Self::Value(_) => Some(IcalParamKind::Value),
Self::Display(_) => Some(IcalParamKind::Display),
Self::Email(_) => Some(IcalParamKind::Email),
Self::Feature(_) => Some(IcalParamKind::Feature),
Self::Label(_) => Some(IcalParamKind::Label),
Self::Order(_) => Some(IcalParamKind::Order),
Self::Schema(_) => Some(IcalParamKind::Schema),
Self::Derived(_) => Some(IcalParamKind::Derived),
Self::ScheduleAgent(_) => Some(IcalParamKind::ScheduleAgent),
Self::ScheduleForceSend(_) => Some(IcalParamKind::ScheduleForceSend),
Self::ScheduleStatus(_) => Some(IcalParamKind::ScheduleStatus),
Self::LinkRel(_) => Some(IcalParamKind::LinkRel),
Self::Gap(_) => Some(IcalParamKind::Gap),
Self::Charset(_) => Some(IcalParamKind::Charset),
Self::Unknown { .. } => None,
}
}
pub fn into_owned(self) -> IcalParam<'static> {
use IcalParam::*;
match self {
AltRep(value) => AltRep(owned(value)),
Cn(value) => Cn(owned(value)),
CuType(value) => CuType(owned(value)),
DelegatedFrom(values) => DelegatedFrom(values.into_iter().map(owned).collect()),
DelegatedTo(values) => DelegatedTo(values.into_iter().map(owned).collect()),
Dir(value) => Dir(owned(value)),
Encoding(value) => Encoding(owned(value)),
FmtType(value) => FmtType(owned(value)),
FbType(value) => FbType(owned(value)),
Language(value) => Language(owned(value)),
Member(values) => Member(values.into_iter().map(owned).collect()),
PartStat(value) => PartStat(owned(value)),
Range(value) => Range(owned(value)),
Related(value) => Related(owned(value)),
RelType(value) => RelType(owned(value)),
Role(value) => Role(owned(value)),
Rsvp(value) => Rsvp(owned(value)),
SentBy(value) => SentBy(owned(value)),
TzId(value) => TzId(owned(value)),
Value(value) => Value(owned(value)),
Display(value) => Display(owned(value)),
Email(value) => Email(owned(value)),
Feature(values) => Feature(values.into_iter().map(owned).collect()),
Label(value) => Label(owned(value)),
Order(value) => Order(owned(value)),
Schema(value) => Schema(owned(value)),
Derived(value) => Derived(owned(value)),
ScheduleAgent(value) => ScheduleAgent(owned(value)),
ScheduleForceSend(value) => ScheduleForceSend(owned(value)),
ScheduleStatus(value) => ScheduleStatus(owned(value)),
LinkRel(value) => LinkRel(owned(value)),
Gap(value) => Gap(owned(value)),
Charset(value) => Charset(owned(value)),
Unknown { name, values } => Unknown {
name: owned(name),
values: values.into_iter().map(owned).collect(),
},
}
}
}
pub(crate) const COMMON_PARAMS: &[IcalParamKind] = &[
IcalParamKind::Value,
IcalParamKind::Language,
IcalParamKind::AltRep,
];
#[cfg(test)]
mod tests {
use core::str::FromStr;
use crate::param::IcalParamKind;
#[test]
fn round_trips_every_kind_through_its_wire_name() {
for kind in [
IcalParamKind::Role,
IcalParamKind::DelegatedFrom,
IcalParamKind::TzId,
] {
assert_eq!(IcalParamKind::from_str(&kind).ok(), Some(kind));
}
assert_eq!(
IcalParamKind::from_str("role").ok(),
Some(IcalParamKind::Role),
);
assert!(IcalParamKind::from_str("X-CUSTOM").is_err());
}
}