[go: up one dir, main page]

calcard/
lib.rs

1/*
2 * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
3 *
4 * SPDX-License-Identifier: Apache-2.0 OR MIT
5 */
6#![doc = include_str!("../README.md")]
7#![deny(rust_2018_idioms)]
8#![forbid(unsafe_code)]
9use common::tokenizer::{StopChar, Token};
10use icalendar::{ICalendar, ICalendarComponentType};
11use std::{
12    borrow::Cow,
13    iter::{Enumerate, Peekable},
14    slice::Iter,
15};
16use vcard::VCard;
17
18pub mod common;
19pub mod datecalc;
20pub mod icalendar;
21#[cfg(feature = "jmap")]
22pub mod jscalendar;
23#[cfg(feature = "jmap")]
24pub mod jscontact;
25pub mod vcard;
26
27#[derive(Debug, Clone, PartialEq, Eq)]
28#[non_exhaustive]
29pub enum Entry {
30    VCard(VCard),
31    ICalendar(ICalendar),
32    InvalidLine(String),
33    UnexpectedComponentEnd {
34        expected: ICalendarComponentType,
35        found: ICalendarComponentType,
36    },
37    UnterminatedComponent(Cow<'static, str>),
38    TooManyComponents,
39    Eof,
40}
41
42pub struct Parser<'x> {
43    pub(crate) input: &'x [u8],
44    pub(crate) iter: Peekable<Enumerate<Iter<'x, u8>>>,
45    pub(crate) strict: bool,
46    pub(crate) stop_colon: bool,
47    pub(crate) stop_semicolon: bool,
48    pub(crate) stop_comma: bool,
49    pub(crate) stop_equal: bool,
50    pub(crate) stop_dot: bool,
51    pub(crate) unfold_qp: bool,
52    pub(crate) unquote: bool,
53    pub(crate) skip_ws: bool,
54    pub(crate) token_buf: Vec<Token<'x>>,
55}
56
57impl<'x> Parser<'x> {
58    pub fn new(input: &'x str) -> Self {
59        let input = input.as_bytes();
60        Self {
61            input,
62            iter: input.iter().enumerate().peekable(),
63            strict: false,
64            stop_colon: true,
65            stop_semicolon: true,
66            stop_comma: true,
67            stop_equal: true,
68            stop_dot: false,
69            unfold_qp: false,
70            unquote: true,
71            skip_ws: false,
72            token_buf: Vec::with_capacity(10),
73        }
74    }
75
76    pub fn strict(mut self) -> Self {
77        self.strict = true;
78        self
79    }
80
81    pub fn entry(&mut self) -> Entry {
82        self.expect_iana_token();
83
84        loop {
85            if let Some(token) = self.token() {
86                if (token.text.eq_ignore_ascii_case(b"BEGIN")
87                    || token.text.eq_ignore_ascii_case("\u{feff}BEGIN".as_bytes()))
88                    && token.stop_char == StopChar::Colon
89                {
90                    if let Some(token) = self.token() {
91                        if token.stop_char == StopChar::Lf {
92                            hashify::fnc_map_ignore_case!(token.text.as_ref(),
93                                b"VCARD" => { return self.vcard(); },
94                                b"VCALENDAR" => { return self.icalendar(ICalendarComponentType::VCalendar); },
95                                b"VEVENT" => { return self.icalendar(ICalendarComponentType::VEvent); },
96                                b"VTODO" => { return self.icalendar(ICalendarComponentType::VTodo); },
97                                b"VJOURNAL" => { return self.icalendar(ICalendarComponentType::VJournal); },
98                                b"VFREEBUSY" => { return self.icalendar(ICalendarComponentType::VFreebusy); },
99                                b"VTIMEZONE" => { return self.icalendar(ICalendarComponentType::VTimezone); },
100                                b"VALARM" => { return self.icalendar(ICalendarComponentType::VAlarm); },
101                                b"STANDARD" => { return self.icalendar(ICalendarComponentType::Standard); },
102                                b"DAYLIGHT" => { return self.icalendar(ICalendarComponentType::Daylight); },
103                                b"VAVAILABILITY" => { return self.icalendar(ICalendarComponentType::VAvailability); },
104                                b"AVAILABLE" => { return self.icalendar(ICalendarComponentType::Available); },
105                                b"PARTICIPANT" => { return self.icalendar(ICalendarComponentType::Participant); },
106                                b"VLOCATION" => { return self.icalendar(ICalendarComponentType::VLocation); },
107                                b"VRESOURCE" => { return self.icalendar(ICalendarComponentType::VResource); },
108                                _ => {
109                                    return self.icalendar(ICalendarComponentType::Other(token.into_string()));
110                                }
111                            )
112                        }
113                    } else {
114                        return Entry::Eof;
115                    }
116                }
117
118                let token_start = token.start;
119                let mut token_end = token.end;
120
121                if token.stop_char != StopChar::Lf {
122                    self.expect_single_value();
123                    while let Some(token) = self.token() {
124                        token_end = token.end;
125                        if token.stop_char == StopChar::Lf {
126                            break;
127                        }
128                    }
129                } else if token.text.is_empty() {
130                    continue;
131                }
132
133                return Entry::InvalidLine(
134                    std::str::from_utf8(
135                        self.input.get(token_start..=token_end).unwrap_or_default(),
136                    )
137                    .unwrap_or_default()
138                    .to_string(),
139                );
140            } else {
141                return Entry::Eof;
142            }
143        }
144    }
145}