1use core::{error, fmt, ops, str};
17
18use alloc::{
19 borrow::Cow,
20 string::{String, ToString},
21 vec::Vec,
22};
23
24use crate::value::owned;
25
26#[derive(Debug)]
28pub struct ParseIcalParamKindError(
29 String,
31);
32
33impl fmt::Display for ParseIcalParamKindError {
34 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35 write!(f, "Cannot parse iCalendar parameter `{}`", self.0)
36 }
37}
38
39impl error::Error for ParseIcalParamKindError {}
40
41#[derive(Clone, Copy, Debug, PartialEq, Eq)]
45pub enum IcalParamKind {
46 AltRep,
48 Cn,
50 CuType,
52 DelegatedFrom,
54 DelegatedTo,
56 Dir,
58 Encoding,
60 FmtType,
62 FbType,
64 Language,
66 Member,
68 PartStat,
70 Range,
72 Related,
74 RelType,
76 Role,
78 Rsvp,
80 SentBy,
82 TzId,
84 Value,
86 Display,
88 Email,
90 Feature,
92 Label,
94 Order,
96 Schema,
98 Derived,
100 ScheduleAgent,
103 ScheduleForceSend,
106 ScheduleStatus,
108 LinkRel,
110 Gap,
112 Charset,
114}
115
116impl IcalParamKind {
117 pub const ALL: [Self; 33] = [
121 Self::AltRep,
122 Self::Cn,
123 Self::CuType,
124 Self::DelegatedFrom,
125 Self::DelegatedTo,
126 Self::Dir,
127 Self::Encoding,
128 Self::FmtType,
129 Self::FbType,
130 Self::Language,
131 Self::Member,
132 Self::PartStat,
133 Self::Range,
134 Self::Related,
135 Self::RelType,
136 Self::Role,
137 Self::Rsvp,
138 Self::SentBy,
139 Self::TzId,
140 Self::Value,
141 Self::Display,
142 Self::Email,
143 Self::Feature,
144 Self::Label,
145 Self::Order,
146 Self::Schema,
147 Self::Derived,
148 Self::ScheduleAgent,
149 Self::ScheduleForceSend,
150 Self::ScheduleStatus,
151 Self::LinkRel,
152 Self::Gap,
153 Self::Charset,
154 ];
155}
156
157impl str::FromStr for IcalParamKind {
158 type Err = ParseIcalParamKindError;
159
160 fn from_str(kind: &str) -> Result<Self, Self::Err> {
162 let kind = match kind {
163 kind if kind.eq_ignore_ascii_case("ALTREP") => Self::AltRep,
164 kind if kind.eq_ignore_ascii_case("CN") => Self::Cn,
165 kind if kind.eq_ignore_ascii_case("CUTYPE") => Self::CuType,
166 kind if kind.eq_ignore_ascii_case("DELEGATED-FROM") => Self::DelegatedFrom,
167 kind if kind.eq_ignore_ascii_case("DELEGATED-TO") => Self::DelegatedTo,
168 kind if kind.eq_ignore_ascii_case("DIR") => Self::Dir,
169 kind if kind.eq_ignore_ascii_case("ENCODING") => Self::Encoding,
170 kind if kind.eq_ignore_ascii_case("FMTTYPE") => Self::FmtType,
171 kind if kind.eq_ignore_ascii_case("FBTYPE") => Self::FbType,
172 kind if kind.eq_ignore_ascii_case("LANGUAGE") => Self::Language,
173 kind if kind.eq_ignore_ascii_case("MEMBER") => Self::Member,
174 kind if kind.eq_ignore_ascii_case("PARTSTAT") => Self::PartStat,
175 kind if kind.eq_ignore_ascii_case("RANGE") => Self::Range,
176 kind if kind.eq_ignore_ascii_case("RELATED") => Self::Related,
177 kind if kind.eq_ignore_ascii_case("RELTYPE") => Self::RelType,
178 kind if kind.eq_ignore_ascii_case("ROLE") => Self::Role,
179 kind if kind.eq_ignore_ascii_case("RSVP") => Self::Rsvp,
180 kind if kind.eq_ignore_ascii_case("SENT-BY") => Self::SentBy,
181 kind if kind.eq_ignore_ascii_case("TZID") => Self::TzId,
182 kind if kind.eq_ignore_ascii_case("VALUE") => Self::Value,
183 kind if kind.eq_ignore_ascii_case("DISPLAY") => Self::Display,
184 kind if kind.eq_ignore_ascii_case("EMAIL") => Self::Email,
185 kind if kind.eq_ignore_ascii_case("FEATURE") => Self::Feature,
186 kind if kind.eq_ignore_ascii_case("LABEL") => Self::Label,
187 kind if kind.eq_ignore_ascii_case("ORDER") => Self::Order,
188 kind if kind.eq_ignore_ascii_case("SCHEMA") => Self::Schema,
189 kind if kind.eq_ignore_ascii_case("DERIVED") => Self::Derived,
190 kind if kind.eq_ignore_ascii_case("SCHEDULE-AGENT") => Self::ScheduleAgent,
191 kind if kind.eq_ignore_ascii_case("SCHEDULE-FORCE-SEND") => Self::ScheduleForceSend,
192 kind if kind.eq_ignore_ascii_case("SCHEDULE-STATUS") => Self::ScheduleStatus,
193 kind if kind.eq_ignore_ascii_case("LINKREL") => Self::LinkRel,
194 kind if kind.eq_ignore_ascii_case("GAP") => Self::Gap,
195 kind if kind.eq_ignore_ascii_case("CHARSET") => Self::Charset,
196 _ => return Err(ParseIcalParamKindError(kind.to_string())),
197 };
198
199 Ok(kind)
200 }
201}
202
203impl ops::Deref for IcalParamKind {
204 type Target = str;
205
206 fn deref(&self) -> &Self::Target {
207 match self {
208 Self::AltRep => "ALTREP",
209 Self::Cn => "CN",
210 Self::CuType => "CUTYPE",
211 Self::DelegatedFrom => "DELEGATED-FROM",
212 Self::DelegatedTo => "DELEGATED-TO",
213 Self::Dir => "DIR",
214 Self::Encoding => "ENCODING",
215 Self::FmtType => "FMTTYPE",
216 Self::FbType => "FBTYPE",
217 Self::Language => "LANGUAGE",
218 Self::Member => "MEMBER",
219 Self::PartStat => "PARTSTAT",
220 Self::Range => "RANGE",
221 Self::Related => "RELATED",
222 Self::RelType => "RELTYPE",
223 Self::Role => "ROLE",
224 Self::Rsvp => "RSVP",
225 Self::SentBy => "SENT-BY",
226 Self::TzId => "TZID",
227 Self::Value => "VALUE",
228 Self::Display => "DISPLAY",
229 Self::Email => "EMAIL",
230 Self::Feature => "FEATURE",
231 Self::Label => "LABEL",
232 Self::Order => "ORDER",
233 Self::Schema => "SCHEMA",
234 Self::Derived => "DERIVED",
235 Self::ScheduleAgent => "SCHEDULE-AGENT",
236 Self::ScheduleForceSend => "SCHEDULE-FORCE-SEND",
237 Self::ScheduleStatus => "SCHEDULE-STATUS",
238 Self::LinkRel => "LINKREL",
239 Self::Gap => "GAP",
240 Self::Charset => "CHARSET",
241 }
242 }
243}
244
245#[derive(Clone, Debug, PartialEq, Eq)]
249pub enum IcalParam<'a> {
250 AltRep(Cow<'a, str>),
252 Cn(Cow<'a, str>),
254 CuType(Cow<'a, str>),
256 DelegatedFrom(Vec<Cow<'a, str>>),
258 DelegatedTo(Vec<Cow<'a, str>>),
260 Dir(Cow<'a, str>),
262 Encoding(Cow<'a, str>),
264 FmtType(Cow<'a, str>),
266 FbType(Cow<'a, str>),
268 Language(Cow<'a, str>),
270 Member(Vec<Cow<'a, str>>),
272 PartStat(Cow<'a, str>),
274 Range(Cow<'a, str>),
276 Related(Cow<'a, str>),
278 RelType(Cow<'a, str>),
280 Role(Cow<'a, str>),
282 Rsvp(Cow<'a, str>),
284 SentBy(Cow<'a, str>),
286 TzId(Cow<'a, str>),
288 Value(Cow<'a, str>),
290 Display(Cow<'a, str>),
292 Email(Cow<'a, str>),
294 Feature(Vec<Cow<'a, str>>),
296 Label(Cow<'a, str>),
298 Order(Cow<'a, str>),
300 Schema(Cow<'a, str>),
302 Derived(Cow<'a, str>),
304 ScheduleAgent(Cow<'a, str>),
306 ScheduleForceSend(Cow<'a, str>),
308 ScheduleStatus(Cow<'a, str>),
310 LinkRel(Cow<'a, str>),
312 Gap(Cow<'a, str>),
314 Charset(Cow<'a, str>),
316
317 Unknown {
319 name: Cow<'a, str>,
321 values: Vec<Cow<'a, str>>,
323 },
324}
325
326impl IcalParam<'_> {
327 pub fn kind(&self) -> Option<IcalParamKind> {
330 match self {
331 Self::AltRep(_) => Some(IcalParamKind::AltRep),
332 Self::Cn(_) => Some(IcalParamKind::Cn),
333 Self::CuType(_) => Some(IcalParamKind::CuType),
334 Self::DelegatedFrom(_) => Some(IcalParamKind::DelegatedFrom),
335 Self::DelegatedTo(_) => Some(IcalParamKind::DelegatedTo),
336 Self::Dir(_) => Some(IcalParamKind::Dir),
337 Self::Encoding(_) => Some(IcalParamKind::Encoding),
338 Self::FmtType(_) => Some(IcalParamKind::FmtType),
339 Self::FbType(_) => Some(IcalParamKind::FbType),
340 Self::Language(_) => Some(IcalParamKind::Language),
341 Self::Member(_) => Some(IcalParamKind::Member),
342 Self::PartStat(_) => Some(IcalParamKind::PartStat),
343 Self::Range(_) => Some(IcalParamKind::Range),
344 Self::Related(_) => Some(IcalParamKind::Related),
345 Self::RelType(_) => Some(IcalParamKind::RelType),
346 Self::Role(_) => Some(IcalParamKind::Role),
347 Self::Rsvp(_) => Some(IcalParamKind::Rsvp),
348 Self::SentBy(_) => Some(IcalParamKind::SentBy),
349 Self::TzId(_) => Some(IcalParamKind::TzId),
350 Self::Value(_) => Some(IcalParamKind::Value),
351 Self::Display(_) => Some(IcalParamKind::Display),
352 Self::Email(_) => Some(IcalParamKind::Email),
353 Self::Feature(_) => Some(IcalParamKind::Feature),
354 Self::Label(_) => Some(IcalParamKind::Label),
355 Self::Order(_) => Some(IcalParamKind::Order),
356 Self::Schema(_) => Some(IcalParamKind::Schema),
357 Self::Derived(_) => Some(IcalParamKind::Derived),
358 Self::ScheduleAgent(_) => Some(IcalParamKind::ScheduleAgent),
359 Self::ScheduleForceSend(_) => Some(IcalParamKind::ScheduleForceSend),
360 Self::ScheduleStatus(_) => Some(IcalParamKind::ScheduleStatus),
361 Self::LinkRel(_) => Some(IcalParamKind::LinkRel),
362 Self::Gap(_) => Some(IcalParamKind::Gap),
363 Self::Charset(_) => Some(IcalParamKind::Charset),
364 Self::Unknown { .. } => None,
365 }
366 }
367
368 pub fn into_owned(self) -> IcalParam<'static> {
372 use IcalParam::*;
373
374 match self {
375 AltRep(value) => AltRep(owned(value)),
376 Cn(value) => Cn(owned(value)),
377 CuType(value) => CuType(owned(value)),
378 DelegatedFrom(values) => DelegatedFrom(values.into_iter().map(owned).collect()),
379 DelegatedTo(values) => DelegatedTo(values.into_iter().map(owned).collect()),
380 Dir(value) => Dir(owned(value)),
381 Encoding(value) => Encoding(owned(value)),
382 FmtType(value) => FmtType(owned(value)),
383 FbType(value) => FbType(owned(value)),
384 Language(value) => Language(owned(value)),
385 Member(values) => Member(values.into_iter().map(owned).collect()),
386 PartStat(value) => PartStat(owned(value)),
387 Range(value) => Range(owned(value)),
388 Related(value) => Related(owned(value)),
389 RelType(value) => RelType(owned(value)),
390 Role(value) => Role(owned(value)),
391 Rsvp(value) => Rsvp(owned(value)),
392 SentBy(value) => SentBy(owned(value)),
393 TzId(value) => TzId(owned(value)),
394 Value(value) => Value(owned(value)),
395 Display(value) => Display(owned(value)),
396 Email(value) => Email(owned(value)),
397 Feature(values) => Feature(values.into_iter().map(owned).collect()),
398 Label(value) => Label(owned(value)),
399 Order(value) => Order(owned(value)),
400 Schema(value) => Schema(owned(value)),
401 Derived(value) => Derived(owned(value)),
402 ScheduleAgent(value) => ScheduleAgent(owned(value)),
403 ScheduleForceSend(value) => ScheduleForceSend(owned(value)),
404 ScheduleStatus(value) => ScheduleStatus(owned(value)),
405 LinkRel(value) => LinkRel(owned(value)),
406 Gap(value) => Gap(owned(value)),
407 Charset(value) => Charset(owned(value)),
408 Unknown { name, values } => Unknown {
409 name: owned(name),
410 values: values.into_iter().map(owned).collect(),
411 },
412 }
413 }
414}
415
416#[cfg(test)]
417mod tests {
418 use core::str::FromStr;
419
420 use crate::param::IcalParamKind;
421
422 #[test]
423 fn round_trips_every_kind_through_its_wire_name() {
424 for kind in [
425 IcalParamKind::Role,
426 IcalParamKind::DelegatedFrom,
427 IcalParamKind::TzId,
428 ] {
429 assert_eq!(IcalParamKind::from_str(&kind).ok(), Some(kind));
430 }
431 assert_eq!(
434 IcalParamKind::from_str("role").ok(),
435 Some(IcalParamKind::Role),
436 );
437 assert!(IcalParamKind::from_str("X-CUSTOM").is_err());
438 }
439}