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