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

            
19
use hurl_core::reader::Reader;
20

            
21
/// Split a `str` into a vec of String params
22
24
pub fn split(s: &str) -> Result<Vec<String>, String> {
23
24
    let mut params = vec![];
24
24
    let mut parser = Parser::new(s);
25
141
    while let Some(param) = parser.param()? {
26
117
        params.push(param);
27
    }
28
24
    Ok(params)
29
}
30

            
31
struct Parser {
32
    reader: Reader,
33
}
34

            
35
impl Parser {
36
24
    fn new(s: &str) -> Parser {
37
24
        let reader = Reader::new(s);
38
24
        Parser { reader }
39
    }
40

            
41
117
    fn delimiter(&mut self) -> Option<(char, bool)> {
42
117
        if self.reader.peek() == Some('\'') {
43
36
            _ = self.reader.read();
44
36
            Some(('\'', false))
45
81
        } else if self.reader.peek() == Some('$') {
46
3
            let save = self.reader.cursor();
47
3
            _ = self.reader.read();
48
3
            if self.reader.peek() == Some('\'') {
49
3
                _ = self.reader.read();
50
3
                Some(('\'', true))
51
            } else {
52
                self.reader.seek(save);
53
                None
54
            }
55
        } else {
56
78
            None
57
        }
58
    }
59

            
60
    fn col(&self) -> usize {
61
        self.reader.cursor().pos.column
62
    }
63

            
64
141
    fn param(&mut self) -> Result<Option<String>, String> {
65
188
        _ = self.reader.read_while(|c| c == ' ');
66
141
        if self.reader.is_eof() {
67
24
            return Ok(None);
68
        }
69
117
        let mut value = String::new();
70
117
        if let Some((delimiter, escaping)) = self.delimiter() {
71
1398
            while let Some(c1) = self.reader.read() {
72
1398
                if c1 == '\\' && escaping {
73
51
                    let c2 = match self.reader.read() {
74
27
                        Some('n') => '\n',
75
                        Some('t') => '\t',
76
                        Some('r') => '\r',
77
24
                        Some(c) => c,
78
                        _ => {
79
                            let col = self.col();
80
                            return Err(format!("Invalid escape at column {col}"));
81
                        }
82
                    };
83
51
                    value.push(c2);
84
1347
                } else if c1 == delimiter {
85
39
                    return Ok(Some(value));
86
1308
                } else {
87
1308
                    value.push(c1);
88
                }
89
            }
90
            let col = self.col();
91
            Err(format!("Missing delimiter {delimiter} at column {col}"))
92
        } else {
93
            loop {
94
717
                match self.reader.read() {
95
                    Some('\\') => {
96
                        if let Some(c) = self.reader.read() {
97
                            value.push(c);
98
                        } else {
99
                            let col = self.col();
100
                            return Err(format!("Invalid escape at column {col}"));
101
                        }
102
                    }
103
69
                    Some(' ') => return Ok(Some(value)),
104
639
                    Some(c) => {
105
639
                        value.push(c);
106
                    }
107
9
                    _ => return Ok(Some(value)),
108
                }
109
            }
110
        }
111
    }
112
}
113

            
114
#[cfg(test)]
115
mod test {
116
    use crate::curl::args;
117
    use crate::curl::args::Parser;
118

            
119
    #[test]
120
    fn test_split() {
121
        let expected = vec!["AAA".to_string(), "BBB".to_string()];
122
        assert_eq!(args::split(r#"AAA BBB"#).unwrap(), expected);
123
        assert_eq!(args::split(r#"AAA  BBB"#).unwrap(), expected);
124
        assert_eq!(args::split(r#" AAA BBB "#).unwrap(), expected);
125
        assert_eq!(args::split(r#"AAA 'BBB'"#).unwrap(), expected);
126
        assert_eq!(args::split(r#"AAA $'BBB'"#).unwrap(), expected);
127

            
128
        let expected = vec!["'".to_string()];
129
        assert_eq!(args::split(r"$'\''").unwrap(), expected);
130
    }
131

            
132
    #[test]
133
    fn test_split_error() {
134
        assert_eq!(
135
            args::split(r#"AAA 'BBB"#).err().unwrap(),
136
            "Missing delimiter ' at column 9".to_string()
137
        );
138
    }
139

            
140
    #[test]
141
    fn test_param_without_quote() {
142
        let mut parser = Parser::new("value");
143
        assert_eq!(parser.param().unwrap().unwrap(), "value".to_string());
144
        assert_eq!(parser.col(), 6);
145

            
146
        let mut parser = Parser::new(" value  ");
147
        assert_eq!(parser.param().unwrap().unwrap(), "value".to_string());
148
        assert_eq!(parser.col(), 8);
149
    }
150

            
151
    #[test]
152
    fn test_param_with_quote() {
153
        let mut parser = Parser::new("'value'");
154
        assert_eq!(parser.param().unwrap().unwrap(), "value".to_string());
155
        assert_eq!(parser.col(), 8);
156

            
157
        let mut parser = Parser::new(" 'value'  ");
158
        assert_eq!(parser.param().unwrap().unwrap(), "value".to_string());
159
        assert_eq!(parser.col(), 9);
160

            
161
        let mut parser = Parser::new("'\\n'");
162
        assert_eq!(parser.param().unwrap().unwrap(), "\\n".to_string());
163
        assert_eq!(parser.col(), 5);
164
    }
165

            
166
    #[test]
167
    fn test_dollar_prefix() {
168
        let mut parser = Parser::new("$'Test: \\''");
169
        assert_eq!(parser.param().unwrap().unwrap(), "Test: '".to_string());
170
        assert_eq!(parser.col(), 12);
171

            
172
        let mut parser = Parser::new("$'\\n'");
173
        assert_eq!(parser.param().unwrap().unwrap(), "\n".to_string());
174
        assert_eq!(parser.col(), 6);
175
    }
176

            
177
    #[test]
178
    fn test_param_missing_closing_quote() {
179
        let mut parser = Parser::new("'value");
180
        assert_eq!(
181
            parser.param().err().unwrap(),
182
            "Missing delimiter ' at column 7".to_string()
183
        );
184
        assert_eq!(parser.col(), 7);
185
    }
186

            
187
    #[test]
188
    fn test_no_more_param() {
189
        assert_eq!(Parser::new("").param().unwrap(), None);
190
        assert_eq!(Parser::new(" ").param().unwrap(), None);
191
    }
192

            
193
    #[test]
194
    fn test_delimiter() {
195
        let mut parser = Parser::new("value");
196
        assert_eq!(parser.delimiter(), None);
197
        assert_eq!(parser.col(), 1);
198
        let mut parser = Parser::new("'value'");
199
        assert_eq!(parser.delimiter().unwrap(), ('\'', false));
200
        assert_eq!(parser.col(), 2);
201
        let mut parser = Parser::new("$'value'");
202
        assert_eq!(parser.delimiter().unwrap(), ('\'', true));
203
        assert_eq!(parser.col(), 3);
204
        let mut parser = Parser::new("$value");
205
        assert_eq!(parser.delimiter(), None);
206
        assert_eq!(parser.col(), 1);
207
    }
208
}