calcard 0.3.1

iCalendar/JSCalendar and vCard/JSContact parsing, building and conversion library for Rust
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
/*
 * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
 *
 * SPDX-License-Identifier: Apache-2.0 OR MIT
 */

use super::*;

impl IanaString for CalendarScale {
    fn as_str(&self) -> &'static str {
        match self {
            CalendarScale::Gregorian => "GREGORIAN",
            CalendarScale::Chinese => "CHINESE",
            CalendarScale::IslamicCivil => "ISLAMIC-CIVIL",
            CalendarScale::Hebrew => "HEBREW",
            CalendarScale::Ethiopic => "ETHIOPIC",
        }
    }
}

#[cfg(feature = "jmap")]
impl CalendarScale {
    pub fn as_js_str(&self) -> &'static str {
        match self {
            CalendarScale::Gregorian => "gregorian",
            CalendarScale::Chinese => "chinese",
            CalendarScale::IslamicCivil => "islamic-civil",
            CalendarScale::Hebrew => "hebrew",
            CalendarScale::Ethiopic => "ethiopic",
        }
    }
}

#[cfg(feature = "rkyv")]
impl IanaString for ArchivedCalendarScale {
    fn as_str(&self) -> &'static str {
        match self {
            ArchivedCalendarScale::Gregorian => "GREGORIAN",
            ArchivedCalendarScale::Chinese => "CHINESE",
            ArchivedCalendarScale::IslamicCivil => "ISLAMIC-CIVIL",
            ArchivedCalendarScale::Hebrew => "HEBREW",
            ArchivedCalendarScale::Ethiopic => "ETHIOPIC",
        }
    }
}

impl IanaParse for CalendarScale {
    fn parse(value: &[u8]) -> Option<Self> {
        hashify::tiny_map_ignore_case!(value,
            "gregorian" => CalendarScale::Gregorian,
            "chinese" => CalendarScale::Chinese,
            "islamic-civil" => CalendarScale::IslamicCivil,
            "hebrew" => CalendarScale::Hebrew,
            "ethiopic" => CalendarScale::Ethiopic,
        )
    }
}

impl Encoding {
    pub fn parse(value: &[u8]) -> Option<Self> {
        hashify::tiny_map_ignore_case!(value,
            b"QUOTED-PRINTABLE" => Encoding::QuotedPrintable,
            b"BASE64" => Encoding::Base64,
            b"Q" => Encoding::QuotedPrintable,
            b"B" => Encoding::Base64,
        )
    }
}

impl PartialDateTime {
    pub fn now() -> Self {
        Self::from_utc_timestamp(chrono::Utc::now().timestamp())
    }

    pub fn from_utc_timestamp(value: i64) -> Self {
        let dt = DateTime::from_timestamp(value);

        PartialDateTime {
            year: dt.year.into(),
            month: dt.month.into(),
            day: dt.day.into(),
            hour: dt.hour.into(),
            minute: dt.minute.into(),
            second: dt.second.into(),
            tz_hour: dt.tz_hour.into(),
            tz_minute: dt.tz_minute.into(),
            tz_minus: false,
        }
    }

    pub fn from_naive_timestamp(value: i64) -> Self {
        let dt = DateTime::from_timestamp(value);

        PartialDateTime {
            year: dt.year.into(),
            month: dt.month.into(),
            day: dt.day.into(),
            hour: dt.hour.into(),
            minute: dt.minute.into(),
            second: dt.second.into(),
            ..Default::default()
        }
    }

    pub fn to_date_time(&self) -> Option<DateTimeResult> {
        let mut dt = DateTimeResult {
            date_time: NaiveDate::from_ymd_opt(
                self.year? as i32,
                self.month? as u32,
                self.day? as u32,
            )?
            .and_hms_opt(
                self.hour.unwrap_or(0) as u32,
                self.minute.unwrap_or(0) as u32,
                self.second.unwrap_or(0) as u32,
            )?,
            offset: None,
        };
        if let Some(tz_hour) = self.tz_hour {
            let secs = (tz_hour as i32 * 3600) + (self.tz_minute.unwrap_or(0) as i32 * 60);
            dt.offset = if self.tz_minus {
                FixedOffset::west_opt(secs)?
            } else {
                FixedOffset::east_opt(secs)?
            }
            .into();
        }
        Some(dt)
    }
}

#[cfg(feature = "rkyv")]
impl ArchivedPartialDateTime {
    pub fn to_date_time(&self) -> Option<DateTimeResult> {
        let mut dt = DateTimeResult {
            date_time: NaiveDate::from_ymd_opt(
                self.year.as_ref()?.to_native() as i32,
                *self.month.as_ref()? as u32,
                *self.day.as_ref()? as u32,
            )?
            .and_hms_opt(
                self.hour.unwrap_or(0) as u32,
                self.minute.unwrap_or(0) as u32,
                self.second.unwrap_or(0) as u32,
            )?,
            offset: None,
        };
        if let Some(tz_hour) = self.tz_hour.as_ref() {
            let secs = (*tz_hour as i32 * 3600) + (self.tz_minute.unwrap_or(0) as i32 * 60);
            dt.offset = if self.tz_minus {
                FixedOffset::west_opt(secs)?
            } else {
                FixedOffset::east_opt(secs)?
            }
            .into();
        }
        Some(dt)
    }
}

impl IanaParse for LinkRelation {
    fn parse(s: &[u8]) -> Option<Self> {
        hashify::map_ignore_case!(s, LinkRelation,
            "about" => LinkRelation::About,
            "acl" => LinkRelation::Acl,
            "alternate" => LinkRelation::Alternate,
            "amphtml" => LinkRelation::Amphtml,
            "api-catalog" => LinkRelation::ApiCatalog,
            "appendix" => LinkRelation::Appendix,
            "apple-touch-icon" => LinkRelation::AppleTouchIcon,
            "apple-touch-startup-image" => LinkRelation::AppleTouchStartupImage,
            "archives" => LinkRelation::Archives,
            "author" => LinkRelation::Author,
            "blocked-by" => LinkRelation::BlockedBy,
            "bookmark" => LinkRelation::Bookmark,
            "c2pa-manifest" => LinkRelation::C2paManifest,
            "canonical" => LinkRelation::Canonical,
            "chapter" => LinkRelation::Chapter,
            "cite-as" => LinkRelation::CiteAs,
            "collection" => LinkRelation::Collection,
            "compression-dictionary" => LinkRelation::CompressionDictionary,
            "contents" => LinkRelation::Contents,
            "convertedfrom" => LinkRelation::Convertedfrom,
            "copyright" => LinkRelation::Copyright,
            "create-form" => LinkRelation::CreateForm,
            "current" => LinkRelation::Current,
            "deprecation" => LinkRelation::Deprecation,
            "describedby" => LinkRelation::Describedby,
            "describes" => LinkRelation::Describes,
            "disclosure" => LinkRelation::Disclosure,
            "dns-prefetch" => LinkRelation::DnsPrefetch,
            "duplicate" => LinkRelation::Duplicate,
            "edit" => LinkRelation::Edit,
            "edit-form" => LinkRelation::EditForm,
            "edit-media" => LinkRelation::EditMedia,
            "enclosure" => LinkRelation::Enclosure,
            "external" => LinkRelation::External,
            "first" => LinkRelation::First,
            "geofeed" => LinkRelation::Geofeed,
            "glossary" => LinkRelation::Glossary,
            "help" => LinkRelation::Help,
            "hosts" => LinkRelation::Hosts,
            "hub" => LinkRelation::Hub,
            "ice-server" => LinkRelation::IceServer,
            "icon" => LinkRelation::Icon,
            "index" => LinkRelation::Index,
            "intervalafter" => LinkRelation::Intervalafter,
            "intervalbefore" => LinkRelation::Intervalbefore,
            "intervalcontains" => LinkRelation::Intervalcontains,
            "intervaldisjoint" => LinkRelation::Intervaldisjoint,
            "intervalduring" => LinkRelation::Intervalduring,
            "intervalequals" => LinkRelation::Intervalequals,
            "intervalfinishedby" => LinkRelation::Intervalfinishedby,
            "intervalfinishes" => LinkRelation::Intervalfinishes,
            "intervalin" => LinkRelation::Intervalin,
            "intervalmeets" => LinkRelation::Intervalmeets,
            "intervalmetby" => LinkRelation::Intervalmetby,
            "intervaloverlappedby" => LinkRelation::Intervaloverlappedby,
            "intervaloverlaps" => LinkRelation::Intervaloverlaps,
            "intervalstartedby" => LinkRelation::Intervalstartedby,
            "intervalstarts" => LinkRelation::Intervalstarts,
            "item" => LinkRelation::Item,
            "last" => LinkRelation::Last,
            "latest-version" => LinkRelation::LatestVersion,
            "license" => LinkRelation::License,
            "linkset" => LinkRelation::Linkset,
            "lrdd" => LinkRelation::Lrdd,
            "manifest" => LinkRelation::Manifest,
            "mask-icon" => LinkRelation::MaskIcon,
            "me" => LinkRelation::Me,
            "media-feed" => LinkRelation::MediaFeed,
            "memento" => LinkRelation::Memento,
            "micropub" => LinkRelation::Micropub,
            "modulepreload" => LinkRelation::Modulepreload,
            "monitor" => LinkRelation::Monitor,
            "monitor-group" => LinkRelation::MonitorGroup,
            "next" => LinkRelation::Next,
            "next-archive" => LinkRelation::NextArchive,
            "nofollow" => LinkRelation::Nofollow,
            "noopener" => LinkRelation::Noopener,
            "noreferrer" => LinkRelation::Noreferrer,
            "opener" => LinkRelation::Opener,
            "openid2.local_id" => LinkRelation::Openid2LocalId,
            "openid2.provider" => LinkRelation::Openid2Provider,
            "original" => LinkRelation::Original,
            "p3pv1" => LinkRelation::P3pv1,
            "payment" => LinkRelation::Payment,
            "pingback" => LinkRelation::Pingback,
            "preconnect" => LinkRelation::Preconnect,
            "predecessor-version" => LinkRelation::PredecessorVersion,
            "prefetch" => LinkRelation::Prefetch,
            "preload" => LinkRelation::Preload,
            "prerender" => LinkRelation::Prerender,
            "prev" => LinkRelation::Prev,
            "preview" => LinkRelation::Preview,
            "previous" => LinkRelation::Previous,
            "prev-archive" => LinkRelation::PrevArchive,
            "privacy-policy" => LinkRelation::PrivacyPolicy,
            "profile" => LinkRelation::Profile,
            "publication" => LinkRelation::Publication,
            "rdap-active" => LinkRelation::RdapActive,
            "rdap-bottom" => LinkRelation::RdapBottom,
            "rdap-down" => LinkRelation::RdapDown,
            "rdap-top" => LinkRelation::RdapTop,
            "rdap-up" => LinkRelation::RdapUp,
            "related" => LinkRelation::Related,
            "restconf" => LinkRelation::Restconf,
            "replies" => LinkRelation::Replies,
            "ruleinput" => LinkRelation::Ruleinput,
            "search" => LinkRelation::Search,
            "section" => LinkRelation::Section,
            "self" => LinkRelation::Self_,
            "service" => LinkRelation::Service,
            "service-desc" => LinkRelation::ServiceDesc,
            "service-doc" => LinkRelation::ServiceDoc,
            "service-meta" => LinkRelation::ServiceMeta,
            "sip-trunking-capability" => LinkRelation::SipTrunkingCapability,
            "sponsored" => LinkRelation::Sponsored,
            "start" => LinkRelation::Start,
            "status" => LinkRelation::Status,
            "stylesheet" => LinkRelation::Stylesheet,
            "subsection" => LinkRelation::Subsection,
            "successor-version" => LinkRelation::SuccessorVersion,
            "sunset" => LinkRelation::Sunset,
            "tag" => LinkRelation::Tag,
            "terms-of-service" => LinkRelation::TermsOfService,
            "timegate" => LinkRelation::Timegate,
            "timemap" => LinkRelation::Timemap,
            "type" => LinkRelation::Type,
            "ugc" => LinkRelation::Ugc,
            "up" => LinkRelation::Up,
            "version-history" => LinkRelation::VersionHistory,
            "via" => LinkRelation::Via,
            "webmention" => LinkRelation::Webmention,
            "working-copy" => LinkRelation::WorkingCopy,
            "working-copy-of" => LinkRelation::WorkingCopyOf,
        )
        .copied()
    }
}

impl IanaString for LinkRelation {
    fn as_str(&self) -> &'static str {
        match self {
            LinkRelation::About => "about",
            LinkRelation::Acl => "acl",
            LinkRelation::Alternate => "alternate",
            LinkRelation::Amphtml => "amphtml",
            LinkRelation::ApiCatalog => "api-catalog",
            LinkRelation::Appendix => "appendix",
            LinkRelation::AppleTouchIcon => "apple-touch-icon",
            LinkRelation::AppleTouchStartupImage => "apple-touch-startup-image",
            LinkRelation::Archives => "archives",
            LinkRelation::Author => "author",
            LinkRelation::BlockedBy => "blocked-by",
            LinkRelation::Bookmark => "bookmark",
            LinkRelation::C2paManifest => "c2pa-manifest",
            LinkRelation::Canonical => "canonical",
            LinkRelation::Chapter => "chapter",
            LinkRelation::CiteAs => "cite-as",
            LinkRelation::Collection => "collection",
            LinkRelation::CompressionDictionary => "compression-dictionary",
            LinkRelation::Contents => "contents",
            LinkRelation::Convertedfrom => "convertedfrom",
            LinkRelation::Copyright => "copyright",
            LinkRelation::CreateForm => "create-form",
            LinkRelation::Current => "current",
            LinkRelation::Deprecation => "deprecation",
            LinkRelation::Describedby => "describedby",
            LinkRelation::Describes => "describes",
            LinkRelation::Disclosure => "disclosure",
            LinkRelation::DnsPrefetch => "dns-prefetch",
            LinkRelation::Duplicate => "duplicate",
            LinkRelation::Edit => "edit",
            LinkRelation::EditForm => "edit-form",
            LinkRelation::EditMedia => "edit-media",
            LinkRelation::Enclosure => "enclosure",
            LinkRelation::External => "external",
            LinkRelation::First => "first",
            LinkRelation::Geofeed => "geofeed",
            LinkRelation::Glossary => "glossary",
            LinkRelation::Help => "help",
            LinkRelation::Hosts => "hosts",
            LinkRelation::Hub => "hub",
            LinkRelation::IceServer => "ice-server",
            LinkRelation::Icon => "icon",
            LinkRelation::Index => "index",
            LinkRelation::Intervalafter => "intervalafter",
            LinkRelation::Intervalbefore => "intervalbefore",
            LinkRelation::Intervalcontains => "intervalcontains",
            LinkRelation::Intervaldisjoint => "intervaldisjoint",
            LinkRelation::Intervalduring => "intervalduring",
            LinkRelation::Intervalequals => "intervalequals",
            LinkRelation::Intervalfinishedby => "intervalfinishedby",
            LinkRelation::Intervalfinishes => "intervalfinishes",
            LinkRelation::Intervalin => "intervalin",
            LinkRelation::Intervalmeets => "intervalmeets",
            LinkRelation::Intervalmetby => "intervalmetby",
            LinkRelation::Intervaloverlappedby => "intervaloverlappedby",
            LinkRelation::Intervaloverlaps => "intervaloverlaps",
            LinkRelation::Intervalstartedby => "intervalstartedby",
            LinkRelation::Intervalstarts => "intervalstarts",
            LinkRelation::Item => "item",
            LinkRelation::Last => "last",
            LinkRelation::LatestVersion => "latest-version",
            LinkRelation::License => "license",
            LinkRelation::Linkset => "linkset",
            LinkRelation::Lrdd => "lrdd",
            LinkRelation::Manifest => "manifest",
            LinkRelation::MaskIcon => "mask-icon",
            LinkRelation::Me => "me",
            LinkRelation::MediaFeed => "media-feed",
            LinkRelation::Memento => "memento",
            LinkRelation::Micropub => "micropub",
            LinkRelation::Modulepreload => "modulepreload",
            LinkRelation::Monitor => "monitor",
            LinkRelation::MonitorGroup => "monitor-group",
            LinkRelation::Next => "next",
            LinkRelation::NextArchive => "next-archive",
            LinkRelation::Nofollow => "nofollow",
            LinkRelation::Noopener => "noopener",
            LinkRelation::Noreferrer => "noreferrer",
            LinkRelation::Opener => "opener",
            LinkRelation::Openid2LocalId => "openid2.local_id",
            LinkRelation::Openid2Provider => "openid2.provider",
            LinkRelation::Original => "original",
            LinkRelation::P3pv1 => "p3pv1",
            LinkRelation::Payment => "payment",
            LinkRelation::Pingback => "pingback",
            LinkRelation::Preconnect => "preconnect",
            LinkRelation::PredecessorVersion => "predecessor-version",
            LinkRelation::Prefetch => "prefetch",
            LinkRelation::Preload => "preload",
            LinkRelation::Prerender => "prerender",
            LinkRelation::Prev => "prev",
            LinkRelation::Preview => "preview",
            LinkRelation::Previous => "previous",
            LinkRelation::PrevArchive => "prev-archive",
            LinkRelation::PrivacyPolicy => "privacy-policy",
            LinkRelation::Profile => "profile",
            LinkRelation::Publication => "publication",
            LinkRelation::RdapActive => "rdap-active",
            LinkRelation::RdapBottom => "rdap-bottom",
            LinkRelation::RdapDown => "rdap-down",
            LinkRelation::RdapTop => "rdap-top",
            LinkRelation::RdapUp => "rdap-up",
            LinkRelation::Related => "related",
            LinkRelation::Restconf => "restconf",
            LinkRelation::Replies => "replies",
            LinkRelation::Ruleinput => "ruleinput",
            LinkRelation::Search => "search",
            LinkRelation::Section => "section",
            LinkRelation::Self_ => "self",
            LinkRelation::Service => "service",
            LinkRelation::ServiceDesc => "service-desc",
            LinkRelation::ServiceDoc => "service-doc",
            LinkRelation::ServiceMeta => "service-meta",
            LinkRelation::SipTrunkingCapability => "sip-trunking-capability",
            LinkRelation::Sponsored => "sponsored",
            LinkRelation::Start => "start",
            LinkRelation::Status => "status",
            LinkRelation::Stylesheet => "stylesheet",
            LinkRelation::Subsection => "subsection",
            LinkRelation::SuccessorVersion => "successor-version",
            LinkRelation::Sunset => "sunset",
            LinkRelation::Tag => "tag",
            LinkRelation::TermsOfService => "terms-of-service",
            LinkRelation::Timegate => "timegate",
            LinkRelation::Timemap => "timemap",
            LinkRelation::Type => "type",
            LinkRelation::Ugc => "ugc",
            LinkRelation::Up => "up",
            LinkRelation::VersionHistory => "version-history",
            LinkRelation::Via => "via",
            LinkRelation::Webmention => "webmention",
            LinkRelation::WorkingCopy => "working-copy",
            LinkRelation::WorkingCopyOf => "working-copy-of",
        }
    }
}