basic_cookies/
cookie.rs

1use super::{lalrpop_util, CookieLexer, CookieLexerError, CookieToken};
2use std::fmt::{Display, Error as FormatterError, Formatter};
3
4const BASIC_COOKIE_ERROR_DESCRIPTION: &'static str = "Cookie Parsing Error";
5const INTERNAL_ERROR_DESCRIPTION: &'static str = "Internal Error";
6const PARSE_ERROR_DESCRIPTION: &'static str = "Parse Error";
7
8lalrpop_mod!(cookie_grammar);
9
10#[derive(Debug)]
11pub struct Cookie<'a> {
12    name: &'a str,
13    value: &'a str,
14}
15
16impl<'a> Cookie<'a> {
17    /// Parses an [RFC 6265](https://tools.ietf.org/html/rfc6265.html#section-4.2.1) compliant cookie string.
18    ///
19    /// # Examples
20    ///
21    /// ```
22    /// use basic_cookies::Cookie;
23    ///
24    /// let parsed_cookies = Cookie::parse("cookie1=value1; cookie2=value2").unwrap();
25    ///
26    /// assert_eq!("cookie1", parsed_cookies[0].get_name());
27    /// assert_eq!("value1", parsed_cookies[0].get_value());
28    ///
29    /// assert_eq!("cookie2", parsed_cookies[1].get_name());
30    /// assert_eq!("value2", parsed_cookies[1].get_value());
31    /// ```
32    pub fn parse(input: &'a str) -> Result<Vec<Cookie<'a>>, Error> {
33        Ok(cookie_grammar::CookiesParser::new()
34            .parse(CookieLexer::new(input))
35            .map_err(ParseError::from_lalrpop_parse_error_to_error)?
36            .clone_to_vec()
37            .iter()
38            .rev()
39            .map(|tok| tok.with_str(input))
40            .collect::<Result<Vec<Cookie>, Error>>()?)
41    }
42
43    /// Gets the name of the cookie.
44    ///
45    /// # Examples
46    ///
47    /// ```
48    /// use basic_cookies::Cookie;
49    ///
50    /// let parsed_cookies = Cookie::parse("name=value").unwrap();
51    /// assert_eq!("name", parsed_cookies[0].get_name());
52    /// ```
53    pub fn get_name(&self) -> &'a str {
54        self.name
55    }
56
57    /// Gets the value of the cookie.
58    ///
59    /// # Examples
60    ///
61    /// ```
62    /// use basic_cookies::Cookie;
63    ///
64    /// let parsed_cookies = Cookie::parse("name=value").unwrap();
65    /// assert_eq!("value", parsed_cookies[0].get_value());
66    /// ```
67    pub fn get_value(&self) -> &'a str {
68        self.value
69    }
70}
71
72#[derive(Debug)]
73pub enum Error {
74    InternalError(InternalError),
75    ParseError(ParseError),
76}
77
78impl Display for Error {
79    fn fmt(&self, f: &mut Formatter) -> Result<(), FormatterError> {
80        f.write_str(BASIC_COOKIE_ERROR_DESCRIPTION)?;
81        f.write_str(": ")?;
82        match self {
83            Error::InternalError(err) => err.fmt(f),
84            Error::ParseError(err) => err.fmt(f),
85        }
86    }
87}
88
89impl std::error::Error for Error {
90    fn description(&self) -> &str {
91        BASIC_COOKIE_ERROR_DESCRIPTION
92    }
93
94    fn cause(&self) -> Option<&dyn std::error::Error> {
95        self.source()
96    }
97
98    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
99        match self {
100            Error::InternalError(err) => Some(err),
101            Error::ParseError(err) => Some(err),
102        }
103    }
104}
105
106#[derive(Debug)]
107pub struct InternalError(InternalErrorKind);
108
109impl InternalError {
110    pub(crate) fn to_error(self) -> Error {
111        Error::InternalError(self)
112    }
113}
114
115impl Display for InternalError {
116    fn fmt(&self, f: &mut Formatter) -> Result<(), FormatterError> {
117        f.write_str(INTERNAL_ERROR_DESCRIPTION)
118    }
119}
120
121impl std::error::Error for InternalError {
122    fn description(&self) -> &str {
123        INTERNAL_ERROR_DESCRIPTION
124    }
125
126    fn cause(&self) -> Option<&dyn std::error::Error> {
127        None
128    }
129
130    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
131        None
132    }
133}
134
135#[derive(Debug)]
136enum InternalErrorKind {
137    NonTerminalIndexBeyondBoundaries,
138}
139
140type LalrpopError = lalrpop_util::ParseError<usize, CookieToken, CookieLexerError>;
141
142#[derive(Debug)]
143pub struct ParseError {
144    lalrpop_error: LalrpopError,
145}
146
147impl ParseError {
148    pub(crate) fn from_lalrpop_parse_error_to_error(src: LalrpopError) -> Error {
149        ParseError { lalrpop_error: src }.to_error()
150    }
151
152    fn to_error(self) -> Error {
153        Error::ParseError(self)
154    }
155}
156
157impl Display for ParseError {
158    fn fmt(&self, f: &mut Formatter) -> Result<(), FormatterError> {
159        f.write_str(PARSE_ERROR_DESCRIPTION)?;
160        f.write_str(": ")?;
161        self.lalrpop_error.fmt(f)
162    }
163}
164
165impl std::error::Error for ParseError {
166    fn description(&self) -> &str {
167        PARSE_ERROR_DESCRIPTION
168    }
169
170    fn cause(&self) -> Option<&dyn std::error::Error> {
171        Some(&self.lalrpop_error)
172    }
173
174    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
175        Some(&self.lalrpop_error)
176    }
177}
178
179mod terminals {
180    use super::nonterminals::NonTerminalSpan;
181    use super::Cookie as FullyParsedCookie;
182    use super::{Error, InternalError};
183
184    #[derive(Clone, Debug)]
185    pub struct Cookie {
186        pub(super) key: NonTerminalSpan,
187        pub(super) value: NonTerminalSpan,
188    }
189
190    impl Cookie {
191        pub(super) fn with_str<'a>(&self, data: &'a str) -> Result<FullyParsedCookie<'a>, Error> {
192            Ok(FullyParsedCookie {
193                name: self.key.as_str(data).map_err(InternalError::to_error)?,
194                value: self.value.as_str(data).map_err(InternalError::to_error)?,
195            })
196        }
197    }
198}
199
200mod nonterminals {
201    use super::{InternalError, InternalErrorKind};
202
203    #[derive(Clone, Debug)]
204    pub struct NonTerminalSpan {
205        start: usize,
206        end: usize,
207    }
208
209    impl NonTerminalSpan {
210        pub(crate) fn new(start: usize, end: usize) -> NonTerminalSpan {
211            NonTerminalSpan {
212                start: start,
213                end: end,
214            }
215        }
216
217        pub(crate) fn as_str<'a>(&self, data: &'a str) -> Result<&'a str, InternalError> {
218            match data.get(self.start..self.end) {
219                Some(res) => Ok(res),
220                None => Err(InternalError(
221                    InternalErrorKind::NonTerminalIndexBeyondBoundaries,
222                )),
223            }
224        }
225    }
226}
227
228#[cfg(test)]
229mod tests {
230    use super::Cookie;
231
232    #[test]
233    fn get_name() {
234        const COOKIE_KEY: &'static str = "cookie_key";
235        const COOKIE_VALUE: &'static str = "cookie_value";
236
237        let cookie = Cookie {
238            name: COOKIE_KEY,
239            value: COOKIE_VALUE,
240        };
241
242        assert_eq!(COOKIE_KEY, cookie.get_name());
243    }
244
245    #[test]
246    fn get_value() {
247        const COOKIE_KEY: &'static str = "cookie_key";
248        const COOKIE_VALUE: &'static str = "cookie_value";
249
250        let cookie = Cookie {
251            name: COOKIE_KEY,
252            value: COOKIE_VALUE,
253        };
254
255        assert_eq!(COOKIE_VALUE, cookie.get_value());
256    }
257
258    #[test]
259    fn single_cookie() {
260        const COOKIE_STR: &'static str = "test=1234";
261        let parsed_cookies = Cookie::parse(COOKIE_STR).unwrap();
262        assert_eq!(1, parsed_cookies.len());
263
264        let parsed_cookie = &parsed_cookies[0];
265        assert_eq!("test", parsed_cookie.name);
266        assert_eq!("1234", parsed_cookie.value);
267    }
268
269    #[test]
270    fn single_cookie_quoted() {
271        const COOKIE_STR: &'static str = "quoted_test=\"quotedval\"";
272        let parsed_cookies = Cookie::parse(COOKIE_STR).unwrap();
273        assert_eq!(1, parsed_cookies.len());
274
275        let parsed_cookie = &parsed_cookies[0];
276        assert_eq!("quoted_test", parsed_cookie.name);
277        assert_eq!("quotedval", parsed_cookie.value);
278    }
279
280    #[test]
281    fn single_cookie_with_equals_in_value() {
282        const COOKIE_STR: &'static str = "test=abc=123";
283        let parsed_cookies = Cookie::parse(COOKIE_STR).unwrap();
284        assert_eq!(1, parsed_cookies.len());
285
286        let parsed_cookie = &parsed_cookies[0];
287        assert_eq!("test", parsed_cookie.name);
288        assert_eq!("abc=123", parsed_cookie.value);
289    }
290
291    #[test]
292    fn single_cookie_ows_before() {
293        const COOKIE_STR: &'static str = " \x09 ztest=9876";
294        let parsed_cookies = Cookie::parse(COOKIE_STR).unwrap();
295        assert_eq!(1, parsed_cookies.len());
296
297        let parsed_cookie = &parsed_cookies[0];
298        assert_eq!("ztest", parsed_cookie.name);
299        assert_eq!("9876", parsed_cookie.value);
300    }
301
302    #[test]
303    fn single_cookie_ows_with_single_space_before() {
304        const COOKIE_STR: &'static str = " qtest=9878";
305        let parsed_cookies = Cookie::parse(COOKIE_STR).unwrap();
306        assert_eq!(1, parsed_cookies.len());
307
308        let parsed_cookie = &parsed_cookies[0];
309        assert_eq!("qtest", parsed_cookie.name);
310        assert_eq!("9878", parsed_cookie.value);
311    }
312
313    #[test]
314    fn single_cookie_ows_after() {
315        const COOKIE_STR: &'static str = "abcde=77766test \x09\x09    ";
316        let parsed_cookies = Cookie::parse(COOKIE_STR).unwrap();
317        assert_eq!(1, parsed_cookies.len());
318
319        let parsed_cookie = &parsed_cookies[0];
320        assert_eq!("abcde", parsed_cookie.name);
321        assert_eq!("77766test", parsed_cookie.value);
322    }
323
324    #[test]
325    fn single_cookie_ows_with_single_space_after() {
326        const COOKIE_STR: &'static str = "xyzzz=test3 ";
327        let parsed_cookies = Cookie::parse(COOKIE_STR).unwrap();
328        assert_eq!(1, parsed_cookies.len());
329
330        let parsed_cookie = &parsed_cookies[0];
331        assert_eq!("xyzzz", parsed_cookie.name);
332        assert_eq!("test3", parsed_cookie.value);
333    }
334
335    #[test]
336    fn single_cookie_ows_before_and_after() {
337        const COOKIE_STR: &'static str = " \x09 ztest=9876       ";
338        let parsed_cookies = Cookie::parse(COOKIE_STR).unwrap();
339        assert_eq!(1, parsed_cookies.len());
340
341        let parsed_cookie = &parsed_cookies[0];
342        assert_eq!("ztest", parsed_cookie.name);
343        assert_eq!("9876", parsed_cookie.value);
344    }
345
346    #[test]
347    fn single_cookie_empty_name() {
348        const COOKIE_STR: &'static str = "=nokey";
349        let parsed_cookies = Cookie::parse(COOKIE_STR).unwrap();
350        assert_eq!(1, parsed_cookies.len());
351
352        let parsed_cookie = &parsed_cookies[0];
353        assert_eq!("", parsed_cookie.name);
354        assert_eq!("nokey", parsed_cookie.value);
355    }
356
357    #[test]
358    fn single_cookie_empty_name_with_ows_before() {
359        const COOKIE_STR: &'static str = " =nokey";
360        let parsed_cookies = Cookie::parse(COOKIE_STR).unwrap();
361        assert_eq!(1, parsed_cookies.len());
362
363        let parsed_cookie = &parsed_cookies[0];
364        assert_eq!("", parsed_cookie.name);
365        assert_eq!("nokey", parsed_cookie.value);
366    }
367
368    #[test]
369    fn single_cookie_empty_value() {
370        const COOKIE_STR: &'static str = "noval=";
371        let parsed_cookies = Cookie::parse(COOKIE_STR).unwrap();
372        assert_eq!(1, parsed_cookies.len());
373
374        let parsed_cookie = &parsed_cookies[0];
375        assert_eq!("noval", parsed_cookie.name);
376        assert_eq!("", parsed_cookie.value);
377    }
378
379    #[test]
380    fn single_cookie_empty_value_with_ows_after() {
381        const COOKIE_STR: &'static str = "noval= ";
382        let parsed_cookies = Cookie::parse(COOKIE_STR).unwrap();
383        assert_eq!(1, parsed_cookies.len());
384
385        let parsed_cookie = &parsed_cookies[0];
386        assert_eq!("noval", parsed_cookie.name);
387        assert_eq!("", parsed_cookie.value);
388    }
389
390    #[test]
391    fn single_cookie_empty_name_and_val() {
392        const COOKIE_STR: &'static str = "=";
393        let parsed_cookies = Cookie::parse(COOKIE_STR).unwrap();
394        assert_eq!(1, parsed_cookies.len());
395
396        let parsed_cookie = &parsed_cookies[0];
397        assert_eq!("", parsed_cookie.name);
398        assert_eq!("", parsed_cookie.value);
399    }
400
401    #[test]
402    fn single_cookie_empty_name_no_equals() {
403        const COOKIE_STR: &'static str = "nokey";
404        let parsed_cookies = Cookie::parse(COOKIE_STR).unwrap();
405        assert_eq!(1, parsed_cookies.len());
406
407        let parsed_cookie = &parsed_cookies[0];
408        assert_eq!("", parsed_cookie.name);
409        assert_eq!("nokey", parsed_cookie.value);
410    }
411
412    #[test]
413    fn two_cookies() {
414        const COOKIE_STR: &'static str = "test1=01234; test2=testval";
415        let parsed_cookies = Cookie::parse(COOKIE_STR).unwrap();
416        assert_eq!(2, parsed_cookies.len());
417
418        let parsed_cookie_0 = &parsed_cookies[0];
419        assert_eq!("test1", parsed_cookie_0.name);
420        assert_eq!("01234", parsed_cookie_0.value);
421
422        let parsed_cookie_1 = &parsed_cookies[1];
423        assert_eq!("test2", parsed_cookie_1.name);
424        assert_eq!("testval", parsed_cookie_1.value);
425    }
426
427    #[test]
428    fn three_cookies() {
429        const COOKIE_STR: &'static str = "test1=0x1234; test2=test2; third_val=v4lue";
430        let parsed_cookies = Cookie::parse(COOKIE_STR).unwrap();
431        assert_eq!(3, parsed_cookies.len());
432
433        let parsed_cookie_0 = &parsed_cookies[0];
434        assert_eq!("test1", parsed_cookie_0.name);
435        assert_eq!("0x1234", parsed_cookie_0.value);
436
437        let parsed_cookie_1 = &parsed_cookies[1];
438        assert_eq!("test2", parsed_cookie_1.name);
439        assert_eq!("test2", parsed_cookie_1.value);
440
441        let parsed_cookie_2 = &parsed_cookies[2];
442        assert_eq!("third_val", parsed_cookie_2.name);
443        assert_eq!("v4lue", parsed_cookie_2.value);
444    }
445
446    #[test]
447    fn three_cookies_ows_before() {
448        const COOKIE_STR: &'static str = " test1=0x1234; test2=test2; third_val=v4lue";
449        let parsed_cookies = Cookie::parse(COOKIE_STR).unwrap();
450        assert_eq!(3, parsed_cookies.len());
451
452        let parsed_cookie_0 = &parsed_cookies[0];
453        assert_eq!("test1", parsed_cookie_0.name);
454        assert_eq!("0x1234", parsed_cookie_0.value);
455
456        let parsed_cookie_1 = &parsed_cookies[1];
457        assert_eq!("test2", parsed_cookie_1.name);
458        assert_eq!("test2", parsed_cookie_1.value);
459
460        let parsed_cookie_2 = &parsed_cookies[2];
461        assert_eq!("third_val", parsed_cookie_2.name);
462        assert_eq!("v4lue", parsed_cookie_2.value);
463    }
464
465    #[test]
466    fn three_cookies_ows_after() {
467        const COOKIE_STR: &'static str = "test1=0x1234; test2=test2; third_val=v4lue   ";
468        let parsed_cookies = Cookie::parse(COOKIE_STR).unwrap();
469        assert_eq!(3, parsed_cookies.len());
470
471        let parsed_cookie_0 = &parsed_cookies[0];
472        assert_eq!("test1", parsed_cookie_0.name);
473        assert_eq!("0x1234", parsed_cookie_0.value);
474
475        let parsed_cookie_1 = &parsed_cookies[1];
476        assert_eq!("test2", parsed_cookie_1.name);
477        assert_eq!("test2", parsed_cookie_1.value);
478
479        let parsed_cookie_2 = &parsed_cookies[2];
480        assert_eq!("third_val", parsed_cookie_2.name);
481        assert_eq!("v4lue", parsed_cookie_2.value);
482    }
483
484    #[test]
485    fn three_cookies_ows_before_and_after() {
486        const COOKIE_STR: &'static str = "   test1=0x1234; test2=test2; third_val=v4lue ";
487        let parsed_cookies = Cookie::parse(COOKIE_STR).unwrap();
488        assert_eq!(3, parsed_cookies.len());
489
490        let parsed_cookie_0 = &parsed_cookies[0];
491        assert_eq!("test1", parsed_cookie_0.name);
492        assert_eq!("0x1234", parsed_cookie_0.value);
493
494        let parsed_cookie_1 = &parsed_cookies[1];
495        assert_eq!("test2", parsed_cookie_1.name);
496        assert_eq!("test2", parsed_cookie_1.value);
497
498        let parsed_cookie_2 = &parsed_cookies[2];
499        assert_eq!("third_val", parsed_cookie_2.name);
500        assert_eq!("v4lue", parsed_cookie_2.value);
501    }
502
503    #[test]
504    fn three_cookies_no_spacing() {
505        const COOKIE_STR: &'static str = "test1=0x1234;test2=test2;third_val=v4lue";
506        let parsed_cookies = Cookie::parse(COOKIE_STR).unwrap();
507        assert_eq!(3, parsed_cookies.len());
508
509        let parsed_cookie_0 = &parsed_cookies[0];
510        assert_eq!("test1", parsed_cookie_0.name);
511        assert_eq!("0x1234", parsed_cookie_0.value);
512
513        let parsed_cookie_1 = &parsed_cookies[1];
514        assert_eq!("test2", parsed_cookie_1.name);
515        assert_eq!("test2", parsed_cookie_1.value);
516
517        let parsed_cookie_2 = &parsed_cookies[2];
518        assert_eq!("third_val", parsed_cookie_2.name);
519        assert_eq!("v4lue", parsed_cookie_2.value);
520    }
521}