ical/
property.rs

1//! Parse the result of `LineReader` into parts.
2//!
3//! Split the result of `LineReader` into property. A property contains:
4//! - A name formated in uppercase.
5//! - An optional list of parameters represented by a vector of `(key/value)` tuple . The key is
6//! formatted in uppercase and the value stay untouched.
7//! - A value stay untouched.
8//!
9//! It work for both the Vcard and Ical format.
10//!
11//! #### Warning
12//!   The parsers `PropertyParser` only parse the content and set to uppercase the case-insensitive
13//!   fields. No checks are made on the fields validity.
14//!
15//! # Examples
16//!
17//! ```toml
18//! [dependencies.ical]
19//! version = "0.3.*"
20//! default-features = false
21//! features = ["property"]
22//! ```
23//!
24//! ```rust
25//! extern crate ical;
26//!
27//! use std::io::BufReader;
28//! use std::fs::File;
29//!
30//! let buf = BufReader::new(File::open("./tests/ressources/vcard_input.vcf")
31//!     .unwrap());
32//!
33//! let reader = ical::PropertyParser::from_reader(buf);
34//!
35//! for line in reader {
36//!     println!("{:?}", line);
37//! }
38//! ```
39
40// Sys mods
41use std::fmt;
42use std::io::BufRead;
43use std::iter::Iterator;
44
45#[cfg(feature = "serde-derive")]
46extern crate serde;
47
48// Internal mods
49use crate::line::{Line, LineReader};
50
51#[derive(Debug, Error)]
52pub enum PropertyError {
53    #[error("Line {}: Missing property name.", line)]
54    MissingName { line: usize },
55    #[error("Line {}: Missing a closing quote.", line)]
56    MissingClosingQuote { line: usize },
57    #[error("Line {}: Missing a \"{}\" delimiter.", line, delimiter)]
58    MissingDelimiter { line: usize, delimiter: char },
59    #[error("Line {}: Missing content after \"{}\".", line, letter)]
60    MissingContentAfter { line: usize, letter: char },
61    #[error("Line {}: Missing a parameter key.", line)]
62    MissingParamKey { line: usize },
63}
64
65/// A VCARD/ICAL property.
66#[derive(Debug, Clone, Default)]
67#[cfg_attr(feature = "serde-derive", derive(serde::Serialize, serde::Deserialize))]
68pub struct Property {
69    /// Property name.
70    pub name: String,
71    /// Property list of parameters.
72    pub params: Option<Vec<(String, Vec<String>)>>,
73    /// Property value.
74    pub value: Option<String>,
75}
76
77impl Property {
78    /// Return a new `Property` object.
79    pub fn new() -> Property {
80        Property {
81            name: String::new(),
82            params: None,
83            value: None,
84        }
85    }
86}
87
88impl fmt::Display for Property {
89    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
90        write!(
91            f,
92            "name: {}\nparams: {:?}\nvalue: {:?}",
93            self.name, self.params, self.value
94        )
95    }
96}
97
98/// Take a `LineReader` and return a list of `Property`.
99#[derive(Debug, Clone)]
100pub struct PropertyParser<B> {
101    line_reader: LineReader<B>,
102}
103
104impl<B: BufRead> PropertyParser<B> {
105    /// Return a new `PropertyParser` from a `LineReader`.
106    pub fn new(line_reader: LineReader<B>) -> PropertyParser<B> {
107        PropertyParser { line_reader }
108    }
109
110    /// Return a new `PropertyParser` from a `Reader`.
111    pub fn from_reader(reader: B) -> PropertyParser<B> {
112        let line_reader = LineReader::new(reader);
113
114        PropertyParser { line_reader }
115    }
116
117    fn parse(&self, line: Line) -> Result<Property, PropertyError> {
118        let mut property = Property::new();
119
120        let mut to_parse = line.as_str();
121
122        // Parse name.
123        let end_name_index;
124
125        let mut param_index = to_parse
126            .find(::PARAM_DELIMITER)
127            .unwrap_or(usize::max_value());
128        let mut value_index = to_parse
129            .find(::VALUE_DELIMITER)
130            .unwrap_or(usize::max_value());
131
132        if param_index < value_index && param_index != 0 {
133            end_name_index = param_index;
134        } else if value_index != usize::max_value() && value_index != 0 {
135            end_name_index = value_index;
136        } else {
137            return Err(PropertyError::MissingName {
138                line: line.number(),
139            })
140            .into();
141        }
142
143        {
144            let split = to_parse.split_at(end_name_index);
145            property.name = split.0.to_string();
146            to_parse = split.1;
147        }
148
149        // Parse parameters.
150        value_index = to_parse
151            .find(::VALUE_DELIMITER)
152            .unwrap_or(usize::max_value());
153        param_index = to_parse
154            .find(::PARAM_DELIMITER)
155            .unwrap_or(usize::max_value());
156
157        // If there is a PARAM_DELIMITER and it not after the VALUE_DELIMITER
158        // there is arguments.
159        if param_index != usize::max_value() && value_index > param_index {
160            let mut param_list = Vec::new();
161
162            while to_parse.starts_with(::PARAM_DELIMITER) {
163                to_parse = to_parse.trim_start_matches(::PARAM_DELIMITER);
164
165                // Split the param key and the rest of the line
166                let mut param_elements = to_parse.splitn(2, ::PARAM_NAME_DELIMITER);
167
168                let key = param_elements
169                    .next()
170                    .and_then(|key| {
171                        if key.is_empty() {
172                            return None;
173                        }
174
175                        Some(key)
176                    })
177                    .ok_or_else(|| PropertyError::MissingParamKey {
178                        line: line.number(),
179                    })?;
180
181                to_parse =
182                    param_elements
183                        .next()
184                        .ok_or_else(|| PropertyError::MissingDelimiter {
185                            delimiter: ::PARAM_NAME_DELIMITER,
186                            line: line.number(),
187                        })?;
188
189                let mut values = Vec::new();
190
191                let mut i = 10;
192
193                // Parse parameter value.
194                while i > 0 {
195                    i -= 1;
196                    if to_parse.starts_with('"') {
197                        // This is a dquoted value. (NAME:Foo="Bar":value)
198                        let mut elements = to_parse.splitn(3, ::PARAM_QUOTE).skip(1);
199                        // unwrap is safe here as we have already check above if there is on '"'.
200                        values.push(
201                            elements
202                                .next()
203                                .ok_or_else(|| PropertyError::MissingClosingQuote {
204                                    line: line.number(),
205                                })?
206                                .to_string(),
207                        );
208
209                        to_parse =
210                            elements
211                                .next()
212                                .ok_or_else(|| PropertyError::MissingClosingQuote {
213                                    line: line.number(),
214                                })?
215                    } else {
216                        // This is a 'raw' value. (NAME;Foo=Bar:value)
217
218                        // Try to find the next param separator.
219                        let param_delimiter = to_parse
220                            .find(::PARAM_DELIMITER)
221                            .unwrap_or(usize::max_value());
222                        let value_delimiter = to_parse
223                            .find(::VALUE_DELIMITER)
224                            .unwrap_or(usize::max_value());
225                        let param_value_delimiter = to_parse
226                            .find(::PARAM_VALUE_DELIMITER)
227                            .unwrap_or(usize::max_value());
228
229                        let end_param_value = {
230                            if param_value_delimiter < value_delimiter
231                                && param_value_delimiter < param_delimiter
232                            {
233                                Ok(param_value_delimiter)
234                            } else if param_delimiter < value_delimiter
235                                && param_delimiter < param_value_delimiter
236                            {
237                                Ok(param_delimiter)
238                            } else if value_delimiter != usize::max_value() {
239                                Ok(value_delimiter)
240                            } else {
241                                Err(PropertyError::MissingContentAfter {
242                                    letter: ::PARAM_NAME_DELIMITER,
243                                    line: line.number(),
244                                })
245                            }
246                        }?;
247
248                        let elements = to_parse.split_at(end_param_value);
249                        values.push(elements.0.to_string());
250                        to_parse = elements.1;
251                    }
252
253                    if !to_parse.starts_with(::PARAM_VALUE_DELIMITER) {
254                        break;
255                    }
256
257                    to_parse = to_parse.trim_start_matches(::PARAM_VALUE_DELIMITER);
258                }
259
260                param_list.push((key.to_uppercase(), values));
261            }
262
263            property.params = Some(param_list);
264        } else {
265            property.params = None;
266        }
267
268        // Parse value
269        to_parse = to_parse.trim_start_matches(::VALUE_DELIMITER);
270        if to_parse.is_empty() {
271            property.value = None;
272        } else {
273            property.value = Some(to_parse.to_string());
274        }
275
276        Ok(property)
277    }
278}
279
280impl<B: BufRead> Iterator for PropertyParser<B> {
281    type Item = Result<Property, PropertyError>;
282
283    fn next(&mut self) -> Option<Result<Property, PropertyError>> {
284        self.line_reader.next().map(|line| self.parse(line))
285    }
286}