1
/*
2
 * Hurl (https://hurl.dev)
3
 * Copyright (C) 2024 Orange
4
 *
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
8
 *
9
 *          http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 *
17
 */
18
use crate::ast::*;
19
use crate::combinator::optional;
20
use crate::parser::error::*;
21
use crate::parser::primitives::*;
22
use crate::parser::string::*;
23
use crate::parser::ParseResult;
24
use crate::reader::Reader;
25

            
26
545
pub fn cookiepath(reader: &mut Reader) -> ParseResult<CookiePath> {
27
545
    let start = reader.cursor().pos;
28
545

            
29
545
    // We create a specialized reader for the templated, error and created structures are
30
545
    // relative tho the main reader.
31
3209
    let s = reader.read_while(|c| c != '[');
32
545
    let mut template_reader = Reader::with_pos(s.as_str(), start);
33
545
    let name = unquoted_template(&mut template_reader)?;
34
545
    let attribute = optional(cookiepath_attribute, reader)?;
35
540
    Ok(CookiePath { name, attribute })
36
}
37

            
38
545
fn cookiepath_attribute(reader: &mut Reader) -> ParseResult<CookieAttribute> {
39
545
    try_literal("[", reader)?;
40
455
    let space0 = zero_or_more_spaces(reader)?;
41
455
    let name = cookiepath_attribute_name(reader)?;
42
450
    let space1 = zero_or_more_spaces(reader)?;
43
450
    literal("]", reader)?;
44
450
    Ok(CookieAttribute {
45
450
        space0,
46
450
        name,
47
450
        space1,
48
450
    })
49
}
50

            
51
455
fn cookiepath_attribute_name(reader: &mut Reader) -> ParseResult<CookieAttributeName> {
52
455
    let start = reader.cursor().pos;
53
3401
    let s = reader.read_while(|c| c.is_alphabetic() || c == '-');
54
455
    match s.to_lowercase().as_str() {
55
455
        "value" => Ok(CookieAttributeName::Value(s)),
56
440
        "expires" => Ok(CookieAttributeName::Expires(s)),
57
355
        "max-age" => Ok(CookieAttributeName::MaxAge(s)),
58
305
        "domain" => Ok(CookieAttributeName::Domain(s)),
59
260
        "path" => Ok(CookieAttributeName::Path(s)),
60
210
        "secure" => Ok(CookieAttributeName::Secure(s)),
61
70
        "httponly" => Ok(CookieAttributeName::HttpOnly(s)),
62
25
        "samesite" => Ok(CookieAttributeName::SameSite(s)),
63
5
        _ => Err(ParseError::new(
64
5
            start,
65
5
            false,
66
5
            ParseErrorKind::InvalidCookieAttribute,
67
5
        )),
68
    }
69
}
70

            
71
#[cfg(test)]
72
mod tests {
73
    use super::*;
74
    use crate::ast::SourceInfo;
75
    use crate::reader::Pos;
76

            
77
    #[test]
78
    fn cookiepath_simple() {
79
        let mut reader = Reader::new("cookie1");
80
        assert_eq!(
81
            cookiepath(&mut reader).unwrap(),
82
            CookiePath {
83
                name: Template {
84
                    delimiter: None,
85
                    elements: vec![TemplateElement::String {
86
                        value: "cookie1".to_string(),
87
                        encoded: "cookie1".to_string(),
88
                    }],
89
                    source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 8)),
90
                },
91
                attribute: None,
92
            }
93
        );
94
        assert_eq!(reader.cursor().index, 7);
95
    }
96

            
97
    #[test]
98
    fn cookiepath_with_attribute() {
99
        let mut reader = Reader::new("cookie1[Domain]");
100
        assert_eq!(
101
            cookiepath(&mut reader).unwrap(),
102
            CookiePath {
103
                name: Template {
104
                    delimiter: None,
105
                    elements: vec![TemplateElement::String {
106
                        value: "cookie1".to_string(),
107
                        encoded: "cookie1".to_string(),
108
                    }],
109
                    source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 8)),
110
                },
111
                attribute: Some(CookieAttribute {
112
                    space0: Whitespace {
113
                        value: String::new(),
114
                        source_info: SourceInfo::new(Pos::new(1, 9), Pos::new(1, 9)),
115
                    },
116
                    name: CookieAttributeName::Domain("Domain".to_string()),
117
                    space1: Whitespace {
118
                        value: String::new(),
119
                        source_info: SourceInfo::new(Pos::new(1, 15), Pos::new(1, 15)),
120
                    },
121
                }),
122
            }
123
        );
124
        assert_eq!(reader.cursor().index, 15);
125
    }
126

            
127
    #[test]
128
    fn cookiepath_with_template() {
129
        let mut reader = Reader::new("{{name}}[Domain]");
130
        assert_eq!(
131
            cookiepath(&mut reader).unwrap(),
132
            CookiePath {
133
                name: Template {
134
                    delimiter: None,
135
                    elements: vec![TemplateElement::Expression(Expr {
136
                        space0: Whitespace {
137
                            value: String::new(),
138
                            source_info: SourceInfo::new(Pos::new(1, 3), Pos::new(1, 3)),
139
                        },
140
                        variable: Variable {
141
                            name: "name".to_string(),
142
                            source_info: SourceInfo::new(Pos::new(1, 3), Pos::new(1, 7)),
143
                        },
144
                        space1: Whitespace {
145
                            value: String::new(),
146
                            source_info: SourceInfo::new(Pos::new(1, 7), Pos::new(1, 7)),
147
                        },
148
                    })],
149
                    source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 9)),
150
                },
151
                attribute: Some(CookieAttribute {
152
                    space0: Whitespace {
153
                        value: String::new(),
154
                        source_info: SourceInfo::new(Pos::new(1, 10), Pos::new(1, 10)),
155
                    },
156
                    name: CookieAttributeName::Domain("Domain".to_string()),
157
                    space1: Whitespace {
158
                        value: String::new(),
159
                        source_info: SourceInfo::new(Pos::new(1, 16), Pos::new(1, 16)),
160
                    },
161
                }),
162
            }
163
        );
164
        assert_eq!(reader.cursor().index, 16);
165
    }
166

            
167
    #[test]
168
    fn cookiepath_error() {
169
        let mut reader = Reader::new("cookie1[");
170
        let error = cookiepath(&mut reader).err().unwrap();
171
        assert_eq!(error.pos, Pos { line: 1, column: 9 });
172
        assert_eq!(error.kind, ParseErrorKind::InvalidCookieAttribute);
173
        assert!(!error.recoverable);
174

            
175
        let mut reader = Reader::new("cookie1[{{field]");
176
        let error = cookiepath(&mut reader).err().unwrap();
177
        assert_eq!(error.pos, Pos { line: 1, column: 9 });
178
        assert_eq!(error.kind, ParseErrorKind::InvalidCookieAttribute);
179
        assert!(!error.recoverable);
180

            
181
        // Check that errors are well reported with a buffer that have already read data.
182
        let mut reader = Reader::new("xxxx{{cookie[Domain]");
183
        _ = reader.read_while(|c| c == 'x');
184

            
185
        let error = cookiepath(&mut reader).err().unwrap();
186
        assert_eq!(
187
            error.pos,
188
            Pos {
189
                line: 1,
190
                column: 13
191
            }
192
        );
193
        assert_eq!(
194
            error.kind,
195
            ParseErrorKind::Expecting {
196
                value: "}}".to_string()
197
            }
198
        );
199
        assert!(!error.recoverable);
200
    }
201

            
202
    #[test]
203
    fn test_cookiepath_attribute_name() {
204
        let mut reader = Reader::new("Domain");
205
        assert_eq!(
206
            cookiepath_attribute_name(&mut reader).unwrap(),
207
            CookieAttributeName::Domain("Domain".to_string())
208
        );
209
        assert_eq!(reader.cursor().index, 6);
210

            
211
        let mut reader = Reader::new("domain");
212
        assert_eq!(
213
            cookiepath_attribute_name(&mut reader).unwrap(),
214
            CookieAttributeName::Domain("domain".to_string())
215
        );
216
        assert_eq!(reader.cursor().index, 6);
217

            
218
        let mut reader = Reader::new("unknown");
219
        let error = cookiepath_attribute_name(&mut reader).err().unwrap();
220
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
221
        assert_eq!(error.kind, ParseErrorKind::InvalidCookieAttribute);
222
    }
223
}