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
    Predicate, PredicateFunc, PredicateFuncValue, PredicateValue, SourceInfo, Whitespace,
20
};
21
use crate::combinator::choice;
22
use crate::parser::predicate_value::predicate_value;
23
use crate::parser::primitives::{one_or_more_spaces, try_literal, zero_or_more_spaces};
24
use crate::parser::{ParseError, ParseErrorKind, ParseResult};
25
use crate::reader::Reader;
26

            
27
18015
pub fn predicate(reader: &mut Reader) -> ParseResult<Predicate> {
28
18015
    let (not, space0) = predicate_not(reader);
29
18015
    let func = predicate_func(reader)?;
30
17985
    Ok(Predicate {
31
17985
        not,
32
17985
        space0,
33
17985
        predicate_func: func,
34
17985
    })
35
}
36

            
37
// can not fail
38
18015
fn predicate_not(reader: &mut Reader) -> (bool, Whitespace) {
39
18015
    let save = reader.cursor();
40
18015
    let no_whitespace = Whitespace {
41
18015
        value: String::new(),
42
18015
        source_info: SourceInfo {
43
18015
            start: save.pos,
44
18015
            end: save.pos,
45
18015
        },
46
18015
    };
47
18015
    if try_literal("not", reader).is_ok() {
48
900
        match one_or_more_spaces(reader) {
49
900
            Ok(space) => (true, space),
50
            Err(_) => {
51
                reader.seek(save);
52
                (false, no_whitespace)
53
            }
54
        }
55
    } else {
56
17115
        (false, no_whitespace)
57
    }
58
}
59

            
60
18015
fn predicate_func(reader: &mut Reader) -> ParseResult<PredicateFunc> {
61
18015
    let start = reader.cursor();
62
18015
    let value = predicate_func_value(reader)?;
63
17985
    let end = reader.cursor();
64
17985
    Ok(PredicateFunc {
65
17985
        source_info: SourceInfo::new(start.pos, end.pos),
66
17985
        value,
67
17985
    })
68
}
69

            
70
18015
fn predicate_func_value(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
71
18015
    let start = reader.cursor();
72
18015
    match choice(
73
18015
        &[
74
18015
            equal_predicate,
75
18015
            not_equal_predicate,
76
18015
            greater_or_equal_predicate,
77
18015
            greater_predicate,
78
18015
            less_or_equal_predicate,
79
18015
            less_predicate,
80
18015
            start_with_predicate,
81
18015
            end_with_predicate,
82
18015
            contain_predicate,
83
18015
            include_predicate,
84
18015
            match_predicate,
85
18015
            integer_predicate,
86
18015
            float_predicate,
87
18015
            boolean_predicate,
88
18015
            string_predicate,
89
18015
            collection_predicate,
90
18015
            date_predicate,
91
18015
            iso_date_predicate,
92
18015
            exist_predicate,
93
18015
            is_empty_predicate,
94
18015
            is_number_predicate,
95
18015
        ],
96
18015
        reader,
97
18015
    ) {
98
        Err(ParseError {
99
            recoverable: true, ..
100
5
        }) => Err(ParseError::new(start.pos, false, ParseErrorKind::Predicate)),
101
18010
        x => x,
102
    }
103
}
104

            
105
impl PredicateValue {
106
710
    pub fn is_number(&self) -> bool {
107
710
        matches!(self, PredicateValue::Number(_))
108
    }
109
1100
    pub fn is_string(&self) -> bool {
110
1100
        matches!(self, PredicateValue::String(_))
111
    }
112

            
113
440
    pub fn is_bytearray(&self) -> bool {
114
440
        matches!(self, PredicateValue::Hex(_)) | matches!(self, PredicateValue::Base64(_))
115
    }
116

            
117
40
    pub fn is_expression(&self) -> bool {
118
40
        matches!(self, PredicateValue::Placeholder(_))
119
    }
120
}
121

            
122
18015
fn equal_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
123
18015
    try_literal("==", reader)?;
124
13470
    let space0 = zero_or_more_spaces(reader)?;
125
13470
    let value = predicate_value(reader)?;
126
13450
    Ok(PredicateFuncValue::Equal { space0, value })
127
}
128

            
129
4545
fn not_equal_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
130
4545
    try_literal("!=", reader)?;
131
215
    let space0 = zero_or_more_spaces(reader)?;
132
215
    let value = predicate_value(reader)?;
133
215
    Ok(PredicateFuncValue::NotEqual { space0, value })
134
}
135

            
136
4265
fn greater_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
137
4265
    try_literal(">", reader)?;
138
335
    let space0 = zero_or_more_spaces(reader)?;
139
335
    let start = reader.cursor();
140
335
    let value = predicate_value(reader)?;
141
335
    if value.is_number() || value.is_string() || value.is_expression() {
142
335
        Ok(PredicateFuncValue::GreaterThan { space0, value })
143
    } else {
144
        Err(ParseError::new(
145
            start.pos,
146
            false,
147
            ParseErrorKind::PredicateValue,
148
        ))
149
    }
150
}
151

            
152
4330
fn greater_or_equal_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
153
4330
    try_literal(">=", reader)?;
154
65
    let space0 = zero_or_more_spaces(reader)?;
155
65
    let start = reader.cursor();
156
65
    let value = predicate_value(reader)?;
157
65
    if value.is_number() || value.is_string() || value.is_expression() {
158
65
        Ok(PredicateFuncValue::GreaterThanOrEqual { space0, value })
159
    } else {
160
        Err(ParseError::new(
161
            start.pos,
162
            false,
163
            ParseErrorKind::PredicateValue,
164
        ))
165
    }
166
}
167

            
168
3815
fn less_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
169
3815
    try_literal("<", reader)?;
170
195
    let space0 = zero_or_more_spaces(reader)?;
171
195
    let start = reader.cursor();
172
195
    let value = predicate_value(reader)?;
173
195
    if value.is_number() || value.is_string() || value.is_expression() {
174
195
        Ok(PredicateFuncValue::LessThan { space0, value })
175
    } else {
176
        Err(ParseError::new(
177
            start.pos,
178
            false,
179
            ParseErrorKind::PredicateValue,
180
        ))
181
    }
182
}
183

            
184
3930
fn less_or_equal_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
185
3930
    try_literal("<=", reader)?;
186
115
    let space0 = zero_or_more_spaces(reader)?;
187
115
    let start = reader.cursor();
188
115
    let value = predicate_value(reader)?;
189
115
    if value.is_number() || value.is_string() || value.is_expression() {
190
115
        Ok(PredicateFuncValue::LessThanOrEqual { space0, value })
191
    } else {
192
        Err(ParseError::new(
193
            start.pos,
194
            false,
195
            ParseErrorKind::PredicateValue,
196
        ))
197
    }
198
}
199

            
200
3620
fn start_with_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
201
3620
    try_literal("startsWith", reader)?;
202
540
    let space0 = one_or_more_spaces(reader)?;
203
540
    let save = reader.cursor();
204
540
    let value = predicate_value(reader)?;
205
540
    if !value.is_string() && !value.is_bytearray() {
206
5
        return Err(ParseError::new(
207
5
            save.pos,
208
5
            false,
209
5
            ParseErrorKind::PredicateValue,
210
5
        ));
211
    }
212
535
    Ok(PredicateFuncValue::StartWith { space0, value })
213
}
214

            
215
3080
fn end_with_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
216
3080
    try_literal("endsWith", reader)?;
217
130
    let space0 = one_or_more_spaces(reader)?;
218
130
    let save = reader.cursor();
219
130
    let value = predicate_value(reader)?;
220
130
    if !value.is_string() && !value.is_bytearray() {
221
        return Err(ParseError::new(
222
            save.pos,
223
            false,
224
            ParseErrorKind::PredicateValue,
225
        ));
226
    }
227
130
    Ok(PredicateFuncValue::EndWith { space0, value })
228
}
229

            
230
2950
fn contain_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
231
2950
    try_literal("contains", reader)?;
232
270
    let space0 = one_or_more_spaces(reader)?;
233
270
    let save = reader.cursor();
234
270
    let value = predicate_value(reader)?;
235
270
    if !value.is_string() && !value.is_bytearray() {
236
        return Err(ParseError::new(
237
            save.pos,
238
            false,
239
            ParseErrorKind::PredicateValue,
240
        ));
241
    }
242
270
    Ok(PredicateFuncValue::Contain { space0, value })
243
}
244

            
245
2680
fn include_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
246
2680
    try_literal("includes", reader)?;
247
315
    let space0 = one_or_more_spaces(reader)?;
248
315
    let value = predicate_value(reader)?;
249
315
    Ok(PredicateFuncValue::Include { space0, value })
250
}
251

            
252
2365
fn match_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
253
2365
    try_literal("matches", reader)?;
254
360
    let space0 = one_or_more_spaces(reader)?;
255
360
    let save = reader.cursor();
256
360
    let value = predicate_value(reader)?;
257
360
    if !matches!(value, PredicateValue::String(_)) && !matches!(value, PredicateValue::Regex(_)) {
258
        return Err(ParseError::new(
259
            save.pos,
260
            false,
261
            ParseErrorKind::PredicateValue,
262
        ));
263
    }
264
360
    Ok(PredicateFuncValue::Match { space0, value })
265
}
266

            
267
2005
fn integer_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
268
2005
    try_literal("isInteger", reader)?;
269
70
    Ok(PredicateFuncValue::IsInteger)
270
}
271

            
272
1935
fn float_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
273
1935
    try_literal("isFloat", reader)?;
274
95
    Ok(PredicateFuncValue::IsFloat)
275
}
276

            
277
1840
fn boolean_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
278
1840
    try_literal("isBoolean", reader)?;
279
55
    Ok(PredicateFuncValue::IsBoolean)
280
}
281

            
282
1785
fn string_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
283
1785
    try_literal("isString", reader)?;
284
55
    Ok(PredicateFuncValue::IsString)
285
}
286

            
287
1730
fn collection_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
288
1730
    try_literal("isCollection", reader)?;
289
155
    Ok(PredicateFuncValue::IsCollection)
290
}
291

            
292
1575
fn date_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
293
1575
    try_literal("isDate", reader)?;
294
80
    Ok(PredicateFuncValue::IsDate)
295
}
296

            
297
1495
fn iso_date_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
298
1495
    try_literal("isIsoDate", reader)?;
299
155
    Ok(PredicateFuncValue::IsIsoDate)
300
}
301

            
302
1340
fn exist_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
303
1340
    try_literal("exists", reader)?;
304
1055
    Ok(PredicateFuncValue::Exist)
305
}
306

            
307
285
fn is_empty_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
308
285
    try_literal("isEmpty", reader)?;
309
205
    Ok(PredicateFuncValue::IsEmpty)
310
}
311

            
312
80
fn is_number_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
313
80
    try_literal("isNumber", reader)?;
314
75
    Ok(PredicateFuncValue::IsNumber)
315
}
316

            
317
#[cfg(test)]
318
mod tests {
319
    use super::*;
320
    use crate::ast::{
321
        Expr, ExprKind, Float, Number, Placeholder, Template, TemplateElement, Variable, I64,
322
    };
323
    use crate::reader::Pos;
324

            
325
    #[test]
326
    fn test_predicate_not() {
327
        let mut reader = Reader::new("notXX");
328
        assert_eq!(
329
            predicate_not(&mut reader),
330
            (
331
                false,
332
                Whitespace {
333
                    value: String::new(),
334
                    source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
335
                }
336
            )
337
        );
338
        assert_eq!(reader.cursor().pos, Pos { line: 1, column: 1 });
339

            
340
        let mut reader = Reader::new("not XX");
341
        assert_eq!(
342
            predicate_not(&mut reader),
343
            (
344
                true,
345
                Whitespace {
346
                    value: String::from(" "),
347
                    source_info: SourceInfo::new(Pos::new(1, 4), Pos::new(1, 5)),
348
                }
349
            )
350
        );
351
        assert_eq!(reader.cursor().pos, Pos { line: 1, column: 5 });
352
    }
353

            
354
    #[test]
355
    fn test_predicate() {
356
        let mut reader = Reader::new("not == true");
357
        assert_eq!(
358
            predicate(&mut reader).unwrap(),
359
            Predicate {
360
                not: true,
361
                space0: Whitespace {
362
                    value: String::from(" "),
363
                    source_info: SourceInfo::new(Pos::new(1, 4), Pos::new(1, 5)),
364
                },
365
                predicate_func: PredicateFunc {
366
                    source_info: SourceInfo::new(Pos::new(1, 5), Pos::new(1, 12)),
367
                    value: PredicateFuncValue::Equal {
368
                        space0: Whitespace {
369
                            value: String::from(" "),
370
                            source_info: SourceInfo::new(Pos::new(1, 7), Pos::new(1, 8)),
371
                        },
372
                        value: PredicateValue::Bool(true),
373
                    },
374
                },
375
            }
376
        );
377
    }
378

            
379
    #[test]
380
    fn test_predicate_func() {
381
        let mut reader = Reader::new("tata == 1");
382
        let error = predicate_func(&mut reader).err().unwrap();
383
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
384
        assert!(!error.recoverable);
385
        assert_eq!(error.kind, ParseErrorKind::Predicate);
386
    }
387

            
388
    #[test]
389
    fn test_equal_predicate() {
390
        let mut reader = Reader::new("==  true");
391
        assert_eq!(
392
            equal_predicate(&mut reader).unwrap(),
393
            PredicateFuncValue::Equal {
394
                value: PredicateValue::Bool(true),
395
                space0: Whitespace {
396
                    value: String::from("  "),
397
                    source_info: SourceInfo::new(Pos::new(1, 3), Pos::new(1, 5)),
398
                },
399
            }
400
        );
401

            
402
        let mut reader = Reader::new("== 1.1");
403
        assert_eq!(
404
            equal_predicate(&mut reader).unwrap(),
405
            PredicateFuncValue::Equal {
406
                value: PredicateValue::Number(Number::Float(Float {
407
                    value: 1.1,
408
                    encoded: "1.1".to_string(),
409
                })),
410
                space0: Whitespace {
411
                    value: String::from(" "),
412
                    source_info: SourceInfo::new(Pos::new(1, 3), Pos::new(1, 4)),
413
                },
414
            }
415
        );
416

            
417
        let mut reader = Reader::new("== 2");
418
        assert_eq!(
419
            equal_predicate(&mut reader).unwrap(),
420
            PredicateFuncValue::Equal {
421
                value: PredicateValue::Number(Number::Integer(I64::new(2, "2".to_string()))),
422
                space0: Whitespace {
423
                    value: String::from(" "),
424
                    source_info: SourceInfo::new(Pos::new(1, 3), Pos::new(1, 4)),
425
                },
426
            },
427
        );
428

            
429
        let mut reader = Reader::new("== \"Bob\"");
430
        assert_eq!(
431
            equal_predicate(&mut reader).unwrap(),
432
            PredicateFuncValue::Equal {
433
                value: PredicateValue::String(Template {
434
                    delimiter: Some('"'),
435
                    elements: vec![TemplateElement::String {
436
                        value: "Bob".to_string(),
437
                        encoded: "Bob".to_string(),
438
                    }],
439
                    source_info: SourceInfo::new(Pos::new(1, 4), Pos::new(1, 9)),
440
                }),
441
                space0: Whitespace {
442
                    value: String::from(" "),
443
                    source_info: SourceInfo::new(Pos::new(1, 3), Pos::new(1, 4)),
444
                },
445
            }
446
        );
447
    }
448

            
449
    #[test]
450
    fn test_equal_expression_predicate() {
451
        let mut reader = Reader::new("== {{count}}");
452
        assert_eq!(
453
            equal_predicate(&mut reader).unwrap(),
454
            PredicateFuncValue::Equal {
455
                value: PredicateValue::Placeholder(Placeholder {
456
                    space0: Whitespace {
457
                        value: String::new(),
458
                        source_info: SourceInfo::new(Pos::new(1, 6), Pos::new(1, 6)),
459
                    },
460
                    expr: Expr {
461
                        kind: ExprKind::Variable(Variable {
462
                            name: "count".to_string(),
463
                            source_info: SourceInfo::new(Pos::new(1, 6), Pos::new(1, 11)),
464
                        }),
465
                        source_info: SourceInfo::new(Pos::new(1, 6), Pos::new(1, 11)),
466
                    },
467
                    space1: Whitespace {
468
                        value: String::new(),
469
                        source_info: SourceInfo::new(Pos::new(1, 11), Pos::new(1, 11)),
470
                    },
471
                }),
472
                space0: Whitespace {
473
                    value: String::from(" "),
474
                    source_info: SourceInfo::new(Pos::new(1, 3), Pos::new(1, 4)),
475
                },
476
            }
477
        );
478
    }
479

            
480
    #[test]
481
    fn test_start_with_predicate() {
482
        let mut reader = Reader::new("startsWith 2");
483
        let error = start_with_predicate(&mut reader).err().unwrap();
484
        assert_eq!(
485
            error.pos,
486
            Pos {
487
                line: 1,
488
                column: 12,
489
            }
490
        );
491
        assert!(!error.recoverable);
492
        assert_eq!(error.kind, ParseErrorKind::PredicateValue);
493
    }
494

            
495
    #[test]
496
    fn test_date_predicate() {
497
        let mut reader = Reader::new("isDate");
498
        let result = date_predicate(&mut reader);
499
        assert_eq!(result.unwrap(), PredicateFuncValue::IsDate);
500
    }
501
}