1use core::{error, fmt, ops, str};
14
15use alloc::{
16 borrow::Cow,
17 string::{String, ToString},
18 vec::Vec,
19};
20
21#[derive(Debug)]
23pub struct VcardParamKindParseError(
24 String,
26);
27
28impl fmt::Display for VcardParamKindParseError {
29 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30 write!(f, "Cannot parse vCard parameter `{}`", self.0)
31 }
32}
33
34impl error::Error for VcardParamKindParseError {}
35
36#[derive(Clone, Copy, Debug, PartialEq, Eq)]
40pub enum VcardParamKind {
41 AltId,
43 Author,
45 AuthorName,
47 CalScale,
49 Charset,
51 Created,
53 Derived,
55 Encoding,
57 Geo,
59 Jsptr,
62 Label,
64 Language,
66 MediaType,
68 Phonetic,
70 Pid,
72 Pref,
74 PropId,
77 Script,
79 ServiceType,
81 SortAs,
83 Type,
85 Tz,
87 Username,
89 Value,
91}
92
93impl str::FromStr for VcardParamKind {
94 type Err = VcardParamKindParseError;
95
96 fn from_str(kind: &str) -> Result<Self, Self::Err> {
98 let kind = match kind {
99 kind if kind.eq_ignore_ascii_case("ALTID") => Self::AltId,
100 kind if kind.eq_ignore_ascii_case("AUTHOR") => Self::Author,
101 kind if kind.eq_ignore_ascii_case("AUTHOR-NAME") => Self::AuthorName,
102 kind if kind.eq_ignore_ascii_case("CALSCALE") => Self::CalScale,
103 kind if kind.eq_ignore_ascii_case("CHARSET") => Self::Charset,
104 kind if kind.eq_ignore_ascii_case("CREATED") => Self::Created,
105 kind if kind.eq_ignore_ascii_case("DERIVED") => Self::Derived,
106 kind if kind.eq_ignore_ascii_case("ENCODING") => Self::Encoding,
107 kind if kind.eq_ignore_ascii_case("GEO") => Self::Geo,
108 kind if kind.eq_ignore_ascii_case("JSPTR") => Self::Jsptr,
109 kind if kind.eq_ignore_ascii_case("LABEL") => Self::Label,
110 kind if kind.eq_ignore_ascii_case("LANGUAGE") => Self::Language,
111 kind if kind.eq_ignore_ascii_case("MEDIATYPE") => Self::MediaType,
112 kind if kind.eq_ignore_ascii_case("PHONETIC") => Self::Phonetic,
113 kind if kind.eq_ignore_ascii_case("PID") => Self::Pid,
114 kind if kind.eq_ignore_ascii_case("PREF") => Self::Pref,
115 kind if kind.eq_ignore_ascii_case("PROP-ID") => Self::PropId,
116 kind if kind.eq_ignore_ascii_case("SCRIPT") => Self::Script,
117 kind if kind.eq_ignore_ascii_case("SERVICE-TYPE") => Self::ServiceType,
118 kind if kind.eq_ignore_ascii_case("SORT-AS") => Self::SortAs,
119 kind if kind.eq_ignore_ascii_case("TYPE") => Self::Type,
120 kind if kind.eq_ignore_ascii_case("TZ") => Self::Tz,
121 kind if kind.eq_ignore_ascii_case("USERNAME") => Self::Username,
122 kind if kind.eq_ignore_ascii_case("VALUE") => Self::Value,
123 _ => return Err(VcardParamKindParseError(kind.to_string())),
124 };
125
126 Ok(kind)
127 }
128}
129
130impl ops::Deref for VcardParamKind {
131 type Target = str;
132
133 fn deref(&self) -> &Self::Target {
134 match self {
135 Self::AltId => "ALTID",
136 Self::Author => "AUTHOR",
137 Self::AuthorName => "AUTHOR-NAME",
138 Self::CalScale => "CALSCALE",
139 Self::Charset => "CHARSET",
140 Self::Created => "CREATED",
141 Self::Derived => "DERIVED",
142 Self::Encoding => "ENCODING",
143 Self::Geo => "GEO",
144 Self::Jsptr => "JSPTR",
145 Self::Label => "LABEL",
146 Self::Language => "LANGUAGE",
147 Self::MediaType => "MEDIATYPE",
148 Self::Phonetic => "PHONETIC",
149 Self::Pid => "PID",
150 Self::Pref => "PREF",
151 Self::PropId => "PROP-ID",
152 Self::Script => "SCRIPT",
153 Self::ServiceType => "SERVICE-TYPE",
154 Self::SortAs => "SORT-AS",
155 Self::Type => "TYPE",
156 Self::Tz => "TZ",
157 Self::Username => "USERNAME",
158 Self::Value => "VALUE",
159 }
160 }
161}
162
163#[derive(Clone, Debug, PartialEq, Eq)]
165pub enum VcardParam<'a> {
166 AltId(Cow<'a, str>),
168 Author(Cow<'a, str>),
170 AuthorName(Cow<'a, str>),
172 CalScale(Cow<'a, str>),
174 Charset(Cow<'a, str>),
176 Created(Cow<'a, str>),
178 Derived(Cow<'a, str>),
180 Encoding(Cow<'a, str>),
182 Geo(Cow<'a, str>),
184 Jsptr(Cow<'a, str>),
186 Label(Cow<'a, str>),
188 Language(Cow<'a, str>),
190 MediaType(Cow<'a, str>),
192 Phonetic(Cow<'a, str>),
194 Pid(Vec<Cow<'a, str>>),
196 Pref(Cow<'a, str>),
198 PropId(Cow<'a, str>),
200 Script(Cow<'a, str>),
202 ServiceType(Cow<'a, str>),
204 SortAs(Vec<Cow<'a, str>>),
206 Type(Vec<Cow<'a, str>>),
208 Tz(Cow<'a, str>),
210 Username(Cow<'a, str>),
212 Value(Cow<'a, str>),
214 Unknown {
216 name: Cow<'a, str>,
218 values: Vec<Cow<'a, str>>,
220 },
221}
222
223impl VcardParam<'_> {
224 pub fn kind(&self) -> Option<VcardParamKind> {
227 match self {
228 Self::AltId(_) => Some(VcardParamKind::AltId),
229 Self::Author(_) => Some(VcardParamKind::Author),
230 Self::AuthorName(_) => Some(VcardParamKind::AuthorName),
231 Self::CalScale(_) => Some(VcardParamKind::CalScale),
232 Self::Charset(_) => Some(VcardParamKind::Charset),
233 Self::Created(_) => Some(VcardParamKind::Created),
234 Self::Derived(_) => Some(VcardParamKind::Derived),
235 Self::Encoding(_) => Some(VcardParamKind::Encoding),
236 Self::Geo(_) => Some(VcardParamKind::Geo),
237 Self::Jsptr(_) => Some(VcardParamKind::Jsptr),
238 Self::Label(_) => Some(VcardParamKind::Label),
239 Self::Language(_) => Some(VcardParamKind::Language),
240 Self::MediaType(_) => Some(VcardParamKind::MediaType),
241 Self::Phonetic(_) => Some(VcardParamKind::Phonetic),
242 Self::Pid(_) => Some(VcardParamKind::Pid),
243 Self::Pref(_) => Some(VcardParamKind::Pref),
244 Self::PropId(_) => Some(VcardParamKind::PropId),
245 Self::Script(_) => Some(VcardParamKind::Script),
246 Self::ServiceType(_) => Some(VcardParamKind::ServiceType),
247 Self::SortAs(_) => Some(VcardParamKind::SortAs),
248 Self::Type(_) => Some(VcardParamKind::Type),
249 Self::Tz(_) => Some(VcardParamKind::Tz),
250 Self::Username(_) => Some(VcardParamKind::Username),
251 Self::Value(_) => Some(VcardParamKind::Value),
252 Self::Unknown { .. } => None,
253 }
254 }
255}
256
257#[cfg(test)]
258mod tests {
259 use core::str::FromStr;
260
261 use crate::param::VcardParamKind;
262
263 #[test]
264 fn round_trips_every_kind_through_its_wire_name() {
265 for kind in [
266 VcardParamKind::Type,
267 VcardParamKind::SortAs,
268 VcardParamKind::MediaType,
269 ] {
270 assert_eq!(VcardParamKind::from_str(&kind).ok(), Some(kind));
271 }
272 assert_eq!(
275 VcardParamKind::from_str("type").ok(),
276 Some(VcardParamKind::Type),
277 );
278 assert!(VcardParamKind::from_str("X-CUSTOM").is_err());
279 }
280}