calcard/vcard/
mod.rs

1/*
2 * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
3 *
4 * SPDX-License-Identifier: Apache-2.0 OR MIT
5 */
6
7use crate::{
8    Entry, Parser,
9    common::{CalendarScale, Data, IanaParse, IanaString, IanaType, PartialDateTime},
10};
11
12pub mod builder;
13pub mod parser;
14pub mod types;
15pub mod utils;
16pub mod writer;
17
18#[cfg(feature = "rkyv")]
19pub mod rkyv_types;
20#[cfg(feature = "rkyv")]
21pub mod rkyv_utils;
22#[cfg(feature = "rkyv")]
23pub mod rkyv_writer;
24
25#[derive(Debug, Default, Clone, PartialEq, Eq)]
26#[cfg_attr(
27    any(test, feature = "serde"),
28    derive(serde::Serialize, serde::Deserialize)
29)]
30#[cfg_attr(
31    feature = "rkyv",
32    derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
33)]
34#[cfg_attr(feature = "rkyv", rkyv(compare(PartialEq)))]
35pub struct VCard {
36    pub entries: Vec<VCardEntry>,
37}
38
39#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
40pub enum VCardVersion {
41    V2_0 = 20,
42    V2_1 = 21,
43    V3_0 = 30,
44    #[default]
45    V4_0 = 40,
46}
47
48#[derive(Debug, Clone, PartialEq, Eq)]
49#[cfg_attr(
50    any(test, feature = "serde"),
51    derive(serde::Serialize, serde::Deserialize)
52)]
53#[cfg_attr(
54    feature = "rkyv",
55    derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
56)]
57#[cfg_attr(feature = "rkyv", rkyv(compare(PartialEq)))]
58pub struct VCardEntry {
59    pub group: Option<String>,
60    pub name: VCardProperty,
61    pub params: Vec<VCardParameter>,
62    pub values: Vec<VCardValue>,
63}
64
65#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
66#[cfg_attr(
67    any(test, feature = "serde"),
68    derive(serde::Serialize, serde::Deserialize)
69)]
70#[cfg_attr(any(test, feature = "serde"), serde(tag = "type", content = "data"))]
71#[cfg_attr(
72    feature = "rkyv",
73    derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
74)]
75#[cfg_attr(feature = "rkyv", rkyv(compare(PartialEq), derive(Debug)))]
76pub enum VCardProperty {
77    Other(String),
78    Begin,
79    End,
80    Source,        // [RFC6350, Section 6.1.3]
81    Kind,          // [RFC6350, Section 6.1.4]
82    Xml,           // [RFC6350, Section 6.1.5]
83    Fn,            // [RFC6350, Section 6.2.1]
84    N,             // [RFC6350, Section 6.2.2][RFC9554, Section 2.2]
85    Nickname,      // [RFC6350, Section 6.2.3]
86    Photo,         // [RFC6350, Section 6.2.4]
87    Bday,          // [RFC6350, Section 6.2.5]
88    Anniversary,   // [RFC6350, Section 6.2.6]
89    Gender,        // [RFC6350, Section 6.2.7]
90    Adr,           // [RFC6350, Section 6.3.1][RFC9554, Section 2.1]
91    Tel,           // [RFC6350, Section 6.4.1]
92    Email,         // [RFC6350, Section 6.4.2]
93    Impp,          // [RFC6350, Section 6.4.3]
94    Lang,          // [RFC6350, Section 6.4.4]
95    Tz,            // [RFC6350, Section 6.5.1]
96    Geo,           // [RFC6350, Section 6.5.2]
97    Title,         // [RFC6350, Section 6.6.1]
98    Role,          // [RFC6350, Section 6.6.2]
99    Logo,          // [RFC6350, Section 6.6.3]
100    Org,           // [RFC6350, Section 6.6.4]
101    Member,        // [RFC6350, Section 6.6.5]
102    Related,       // [RFC6350, Section 6.6.6]
103    Categories,    // [RFC6350, Section 6.7.1]
104    Note,          // [RFC6350, Section 6.7.2]
105    Prodid,        // [RFC6350, Section 6.7.3]
106    Rev,           // [RFC6350, Section 6.7.4]
107    Sound,         // [RFC6350, Section 6.7.5]
108    Uid,           // [RFC6350, Section 6.7.6]
109    Clientpidmap,  // [RFC6350, Section 6.7.7]
110    Url,           // [RFC6350, Section 6.7.8]
111    Version,       // [RFC6350, Section 6.7.9]
112    Key,           // [RFC6350, Section 6.8.1]
113    Fburl,         // [RFC6350, Section 6.9.1]
114    Caladruri,     // [RFC6350, Section 6.9.2]
115    Caluri,        // [RFC6350, Section 6.9.3]
116    Birthplace,    // [RFC6474, Section 2.1]
117    Deathplace,    // [RFC6474, Section 2.2]
118    Deathdate,     // [RFC6474, Section 2.3]
119    Expertise,     // [RFC6715, Section 2.1]
120    Hobby,         // [RFC6715, Section 2.2]
121    Interest,      // [RFC6715, Section 2.3]
122    OrgDirectory,  // [RFC6715, Section 2.4][RFC Errata 3341]
123    ContactUri,    // [RFC8605, Section 2.1]
124    Created,       // [RFC9554, Section 3.1]
125    Gramgender,    // [RFC9554, Section 3.2]
126    Language,      // [RFC9554, Section 3.3]
127    Pronouns,      // [RFC9554, Section 3.4]
128    Socialprofile, // [RFC9554, Section 3.5]
129    Jsprop,        // [RFC9555, Section 3.2.1]
130}
131
132#[derive(Debug, Clone, PartialEq)]
133#[cfg_attr(
134    any(test, feature = "serde"),
135    derive(serde::Serialize, serde::Deserialize)
136)]
137#[cfg_attr(any(test, feature = "serde"), serde(tag = "type", content = "data"))]
138#[cfg_attr(
139    feature = "rkyv",
140    derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
141)]
142#[cfg_attr(feature = "rkyv", rkyv(compare(PartialEq)))]
143pub enum VCardValue {
144    Text(String),
145    Integer(i64),
146    Float(f64),
147    Boolean(bool),
148    PartialDateTime(PartialDateTime),
149    Binary(Data),
150    Sex(VCardSex),
151    GramGender(VCardGramGender),
152    Kind(VCardKind),
153    Component(Vec<String>),
154}
155
156#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
157#[cfg_attr(
158    any(test, feature = "serde"),
159    derive(serde::Serialize, serde::Deserialize)
160)]
161#[cfg_attr(
162    feature = "rkyv",
163    derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
164)]
165#[cfg_attr(feature = "rkyv", rkyv(compare(PartialEq)))]
166pub struct VCardParameter {
167    pub name: VCardParameterName,
168    pub value: VCardParameterValue,
169}
170
171#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
172#[cfg_attr(
173    any(test, feature = "serde"),
174    derive(serde::Serialize, serde::Deserialize)
175)]
176#[cfg_attr(any(test, feature = "serde"), serde(tag = "type", content = "data"))]
177#[cfg_attr(
178    feature = "rkyv",
179    derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
180)]
181#[cfg_attr(feature = "rkyv", rkyv(compare(PartialEq)))]
182pub enum VCardParameterValue {
183    Text(String),
184    Integer(u32),
185    Timestamp(i64),
186    Bool(bool),
187    ValueType(VCardValueType),
188    Type(VCardType),
189    Calscale(CalendarScale),
190    Level(VCardLevel),
191    Phonetic(VCardPhonetic),
192    Jscomps(Vec<Jscomp>),
193    Null,
194}
195
196#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
197#[cfg_attr(
198    any(test, feature = "serde"),
199    derive(serde::Serialize, serde::Deserialize)
200)]
201#[cfg_attr(
202    feature = "rkyv",
203    derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
204)]
205#[cfg_attr(feature = "rkyv", rkyv(compare(PartialEq), derive(Debug)))]
206pub enum Jscomp {
207    Entry { position: u32, value: u32 },
208    Separator(String),
209}
210
211#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
212#[cfg_attr(
213    any(test, feature = "serde"),
214    derive(serde::Serialize, serde::Deserialize)
215)]
216#[cfg_attr(
217    feature = "rkyv",
218    derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
219)]
220#[cfg_attr(feature = "rkyv", rkyv(compare(PartialEq), derive(PartialEq)))]
221pub enum VCardParameterName {
222    Other(String),
223    Language,    // [RFC6350, Section 5.1]
224    Value,       // [RFC6350, Section 5.2]
225    Pref,        // [RFC6350, Section 5.3]
226    Altid,       // [RFC6350, Section 5.4]
227    Pid,         // [RFC6350, Section 5.5]
228    Type,        // [RFC6350, Section 5.6]
229    Mediatype,   // [RFC6350, Section 5.7]
230    Calscale,    // [RFC6350, Section 5.8]
231    SortAs,      // [RFC6350, Section 5.9]
232    Geo,         // [RFC6350, Section 5.10]
233    Tz,          // [RFC6350, Section 5.11]
234    Index,       // [RFC6715, Section 3.1]
235    Level,       // [RFC6715, Section 3.2]
236    Group,       // [RFC7095, Section 8.1]
237    Cc,          // [RFC8605, Section 3.1]
238    Author,      // [RFC9554, Section 4.1]
239    AuthorName,  // [RFC9554, Section 4.2]
240    Created,     // [RFC9554, Section 4.3]
241    Derived,     // [RFC9554, Section 4.4]
242    Label,       // [RFC6350, Section 6.3.1][RFC9554, Section 4.5]
243    Phonetic,    // [RFC9554, Section 4.6]
244    PropId,      // [RFC9554, Section 4.7]
245    Script,      // [RFC9554, Section 4.8]
246    ServiceType, // [RFC9554, Section 4.9]
247    Username,    // [RFC9554, Section 4.10]
248    Jsptr,       // [RFC9555, Section 3.3.2]
249    Jscomps,     // [RFC9555, Section 3.3.2]
250}
251
252#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
253#[cfg_attr(
254    any(test, feature = "serde"),
255    derive(serde::Serialize, serde::Deserialize)
256)]
257#[cfg_attr(any(test, feature = "serde"), serde(tag = "type", content = "data"))]
258#[cfg_attr(
259    feature = "rkyv",
260    derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
261)]
262#[cfg_attr(feature = "rkyv", rkyv(compare(PartialEq), derive(Debug)))]
263pub enum VCardValueType {
264    Boolean,       // [RFC6350, Section 4.4]
265    Date,          // [RFC6350, Section 4.3.1]
266    DateAndOrTime, // [RFC6350, Section 4.3.4]
267    DateTime,      // [RFC6350, Section 4.3.3]
268    Float,         // [RFC6350, Section 4.6]
269    Integer,       // [RFC6350, Section 4.5]
270    LanguageTag,   // [RFC6350, Section 4.8]
271    Text,          // [RFC6350, Section 4.1]
272    Time,          // [RFC6350, Section 4.3.2]
273    Timestamp,     // [RFC6350, Section 4.3.5]
274    Uri,           // [RFC6350, Section 4.2]
275    UtcOffset,     // [RFC6350, Section 4.7]
276}
277
278#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
279#[cfg_attr(
280    any(test, feature = "serde"),
281    derive(serde::Serialize, serde::Deserialize)
282)]
283#[cfg_attr(
284    feature = "rkyv",
285    derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
286)]
287#[cfg_attr(feature = "rkyv", rkyv(compare(PartialEq), derive(Debug)))]
288pub enum VCardLevel {
289    Beginner, // [RFC6715, Section 3.2]
290    Average,  // [RFC6715, Section 3.2]
291    Expert,   // [RFC6715, Section 3.2]
292    High,     // [RFC6715, Section 3.2]
293    Medium,   // [RFC6715, Section 3.2]
294    Low,      // [RFC6715, Section 3.2]
295}
296
297#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
298#[cfg_attr(
299    any(test, feature = "serde"),
300    derive(serde::Serialize, serde::Deserialize)
301)]
302#[cfg_attr(any(test, feature = "serde"), serde(tag = "type", content = "data"))]
303#[cfg_attr(
304    feature = "rkyv",
305    derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
306)]
307#[cfg_attr(feature = "rkyv", rkyv(compare(PartialEq), derive(Debug)))]
308pub enum VCardPhonetic {
309    Ipa,    // [RFC9554, Section 4.6]
310    Jyut,   // [RFC9554, Section 4.6]
311    Piny,   // [RFC9554, Section 4.6]
312    Script, // [RFC9554, Section 4.6]
313}
314
315#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
316#[cfg_attr(
317    any(test, feature = "serde"),
318    derive(serde::Serialize, serde::Deserialize)
319)]
320#[cfg_attr(any(test, feature = "serde"), serde(tag = "type", content = "data"))]
321#[cfg_attr(
322    feature = "rkyv",
323    derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
324)]
325#[cfg_attr(feature = "rkyv", rkyv(compare(PartialEq), derive(Debug)))]
326pub enum VCardType {
327    Work,         // [RFC6350, Section 5.6]
328    Home,         // [RFC6350, Section 5.6]
329    Billing,      // [RFC9554, Section 5.1]
330    Delivery,     // [RFC9554, Section 5.2]
331    Contact,      // [RFC6350, Section 6.6.6]
332    Acquaintance, // [RFC6350, Section 6.6.6]
333    Friend,       // [RFC6350, Section 6.6.6]
334    Met,          // [RFC6350, Section 6.6.6]
335    CoWorker,     // [RFC6350, Section 6.6.6]
336    Colleague,    // [RFC6350, Section 6.6.6]
337    CoResident,   // [RFC6350, Section 6.6.6]
338    Neighbor,     // [RFC6350, Section 6.6.6]
339    Child,        // [RFC6350, Section 6.6.6]
340    Parent,       // [RFC6350, Section 6.6.6]
341    Sibling,      // [RFC6350, Section 6.6.6]
342    Spouse,       // [RFC6350, Section 6.6.6]
343    Kin,          // [RFC6350, Section 6.6.6]
344    Muse,         // [RFC6350, Section 6.6.6]
345    Crush,        // [RFC6350, Section 6.6.6]
346    Date,         // [RFC6350, Section 6.6.6]
347    Sweetheart,   // [RFC6350, Section 6.6.6]
348    Me,           // [RFC6350, Section 6.6.6]
349    Agent,        // [RFC6350, Section 6.6.6]
350    Emergency,    // [RFC6350, Section 6.6.6]
351    Text,         // [RFC6350, Section 6.4.1]
352    Voice,        // [RFC6350, Section 6.4.1]
353    Fax,          // [RFC6350, Section 6.4.1]
354    Cell,         // [RFC6350, Section 6.4.1]
355    Video,        // [RFC6350, Section 6.4.1]
356    Pager,        // [RFC6350, Section 6.4.1]
357    Textphone,    // [RFC6350, Section 6.4.1]
358    MainNumber,   // [RFC7852]
359}
360
361#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
362#[cfg_attr(
363    any(test, feature = "serde"),
364    derive(serde::Serialize, serde::Deserialize)
365)]
366#[cfg_attr(
367    feature = "rkyv",
368    derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
369)]
370#[cfg_attr(feature = "rkyv", rkyv(compare(PartialEq), derive(Debug)))]
371pub enum VCardGramGender {
372    Animate,   // [RFC9554, Section 3.2]
373    Common,    // [RFC9554, Section 3.2]
374    Feminine,  // [RFC9554, Section 3.2]
375    Inanimate, // [RFC9554, Section 3.2]
376    Masculine, // [RFC9554, Section 3.2]
377    Neuter,    // [RFC9554, Section 3.2]
378}
379
380#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
381#[cfg_attr(
382    any(test, feature = "serde"),
383    derive(serde::Serialize, serde::Deserialize)
384)]
385#[cfg_attr(
386    feature = "rkyv",
387    derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
388)]
389#[cfg_attr(feature = "rkyv", rkyv(compare(PartialEq), derive(Debug)))]
390pub enum VCardSex {
391    Male,
392    Female,
393    Other,
394    NoneOrNotApplicable,
395    Unknown,
396}
397
398#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
399#[cfg_attr(
400    any(test, feature = "serde"),
401    derive(serde::Serialize, serde::Deserialize)
402)]
403#[cfg_attr(
404    feature = "rkyv",
405    derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
406)]
407#[cfg_attr(feature = "rkyv", rkyv(compare(PartialEq), derive(Debug)))]
408pub enum VCardKind {
409    Individual,  // [RFC6350, Section 6.1.4]
410    Group,       // [RFC6350, Section 6.1.4]
411    Org,         // [RFC6350, Section 6.1.4]
412    Location,    // [RFC6350, Section 6.1.4]
413    Application, // [RFC6473, Section 3]
414    Device,      // [RFC6869, Section 3]
415}
416
417#[derive(Debug, Clone, Copy, PartialEq, Eq)]
418pub(crate) enum ValueSeparator {
419    None,
420    Comma,
421    Semicolon,
422    SemicolonAndComma,
423    Skip,
424}
425
426#[derive(Debug, Clone, Copy, PartialEq, Eq)]
427pub(crate) enum ValueType {
428    Vcard(VCardValueType),
429    Kind,
430    Sex,
431    GramGender,
432}
433
434impl ValueType {
435    pub fn unwrap_vcard(self) -> VCardValueType {
436        match self {
437            ValueType::Vcard(v) => v,
438            _ => VCardValueType::Text,
439        }
440    }
441}
442
443impl VCard {
444    pub fn parse(value: impl AsRef<str>) -> Result<Self, Entry> {
445        let mut parser = Parser::new(value.as_ref());
446        match parser.entry() {
447            Entry::VCard(vcard) => Ok(vcard),
448            other => Err(other),
449        }
450    }
451}