Skip to main content

calico/
parser.rs

1//! A [`winnow`]-based RFC 5545 parser implementation.
2
3use std::borrow::Cow;
4
5use winnow::{
6    Parser,
7    ascii::Caseless,
8    error::ParserError,
9    stream::{AsBytes, Compare, Stream, StreamIsPartial},
10    token::take_until,
11};
12
13use error::CalendarParseError;
14
15use crate::parser::escaped::split_fold_prefix;
16
17pub mod component;
18pub mod config;
19pub mod error;
20pub mod escaped;
21pub mod parameter;
22pub mod primitive;
23pub mod property;
24pub mod rrule;
25
26/// An input stream compatible with the parsers in [`calico::parser`](crate::parser).
27pub trait InputStream
28where
29    Self: StreamIsPartial + Stream + Compare<char> + AsRef<[u8]>,
30    for<'a> Self: Compare<Caseless<&'a str>> + Compare<&'a str>,
31{
32    type Str: Clone + AsRef<str> + Into<String> + Into<Box<str>>;
33
34    fn try_into_str(slice: &Self::Slice) -> Result<Self::Str, CalendarParseError<Self::Slice>>;
35    fn try_into_string(slice: &Self::Slice) -> Result<String, CalendarParseError<Self::Slice>>;
36    fn as_bytes(slice: &Self::Slice) -> &[u8];
37    fn str_from_static_str(s: &'static str) -> Self::Str;
38
39    /// Removes as many line folds from the prefix of `self` as possible, and returns the number of
40    /// bytes removed (this will always be a multiple of three).
41    fn strip_line_fold_prefix(&mut self) -> usize;
42
43    /// Returns the longest contiguous prefix of `self`. A slice is contiguous if it does not
44    /// contain line folds or newlines. If the input does not contain the sequence `\r\n` anywhere,
45    /// the entire input is returned as a slice.
46    fn next_contiguous_slice<E>(input: &mut Self) -> Result<Self::Slice, E>
47    where
48        E: ParserError<Self>;
49}
50
51impl InputStream for &str {
52    type Str = Self;
53
54    #[inline(always)]
55    fn try_into_str(slice: &Self::Slice) -> Result<Self::Str, CalendarParseError<Self::Slice>> {
56        Ok(slice)
57    }
58
59    #[inline(always)]
60    fn try_into_string(slice: &Self::Slice) -> Result<String, CalendarParseError<Self::Slice>> {
61        Ok(slice.to_string())
62    }
63
64    #[inline(always)]
65    fn as_bytes(slice: &Self::Slice) -> &[u8] {
66        slice.as_bytes()
67    }
68
69    #[inline(always)]
70    fn str_from_static_str(s: &'static str) -> Self::Str {
71        s
72    }
73
74    #[inline(always)]
75    fn strip_line_fold_prefix(&mut self) -> usize {
76        let (prefix, tail) = split_fold_prefix(self.as_bytes());
77        *self = str::from_utf8(tail).expect("tail represents a valid UTF-8 string slice");
78        prefix.len()
79    }
80
81    #[inline(always)]
82    fn next_contiguous_slice<E>(input: &mut Self) -> Result<Self::Slice, E>
83    where
84        E: ParserError<Self>,
85    {
86        let _ = input.strip_line_fold_prefix();
87
88        match take_until(0.., "\r\n").parse_next(input) {
89            Ok(slice) => Ok(slice),
90            Err(()) => Ok(input.finish()),
91        }
92    }
93}
94
95impl<'a> InputStream for &'a [u8] {
96    type Str = &'a str;
97
98    #[inline(always)]
99    fn try_into_str(slice: &Self::Slice) -> Result<Self::Str, CalendarParseError<Self::Slice>> {
100        std::str::from_utf8(slice).map_err(Into::into)
101    }
102
103    #[inline(always)]
104    fn try_into_string(slice: &Self::Slice) -> Result<String, CalendarParseError<Self::Slice>> {
105        Self::try_into_str(slice).map(ToString::to_string)
106    }
107
108    #[inline(always)]
109    fn as_bytes(slice: &Self::Slice) -> &[u8] {
110        slice
111    }
112
113    #[inline(always)]
114    fn str_from_static_str(s: &'static str) -> Self::Str {
115        s
116    }
117
118    #[inline(always)]
119    fn strip_line_fold_prefix(&mut self) -> usize {
120        let (prefix, tail) = split_fold_prefix(self);
121        *self = tail;
122        prefix.len()
123    }
124
125    #[inline(always)]
126    fn next_contiguous_slice<E>(input: &mut Self) -> Result<Self::Slice, E>
127    where
128        E: ParserError<Self>,
129    {
130        let _ = input.strip_line_fold_prefix();
131
132        match take_until(0.., "\r\n").parse_next(input) {
133            Ok(slice) => Ok(slice),
134            Err(()) => Ok(input.finish()),
135        }
136    }
137}
138
139impl<'a> InputStream for escaped::Escaped<'a> {
140    type Str = Cow<'a, str>;
141
142    #[inline(always)]
143    fn try_into_str(slice: &Self::Slice) -> Result<Self::Str, CalendarParseError<Self::Slice>> {
144        slice.try_into_cow_str().map_err(Into::into)
145    }
146
147    #[inline(always)]
148    fn try_into_string(slice: &Self::Slice) -> Result<String, CalendarParseError<Self::Slice>> {
149        Self::try_into_str(slice).map(|s| s.into_owned())
150    }
151
152    #[inline(always)]
153    fn as_bytes(slice: &Self::Slice) -> &[u8] {
154        slice.as_bytes()
155    }
156
157    #[inline(always)]
158    fn str_from_static_str(s: &'static str) -> Self::Str {
159        Cow::Borrowed(s)
160    }
161
162    #[inline(always)]
163    fn strip_line_fold_prefix(&mut self) -> usize {
164        let (prefix, tail) = split_fold_prefix(self.0);
165        self.0 = tail;
166        prefix.len()
167    }
168
169    #[inline(always)]
170    fn next_contiguous_slice<E>(input: &mut Self) -> Result<Self::Slice, E>
171    where
172        E: ParserError<Self>,
173    {
174        // the semantics are slightly different here, because the line fold sequence does not occur
175        // as part of the Escaped stream; rather it occurs as part of the underlying byte slice.
176
177        let _ = input.strip_line_fold_prefix();
178        let indices = <&[u8] as winnow::stream::FindSlice<_>>::find_slice(&input.0, "\r\n");
179
180        match indices {
181            Some(i) => Ok(input.next_slice(i.start)),
182            None => Ok(input.finish()),
183        }
184    }
185}