1
/*
2
 * Hurl (https://hurl.dev)
3
 * Copyright (C) 2026 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::reader::Reader;
23

            
24
use super::ParseResult;
25
use super::error::{ParseError, ParseErrorKind};
26
use super::predicate_value::predicate_value;
27
use super::primitives::{one_or_more_spaces, try_literal, zero_or_more_spaces};
28

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

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

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

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

            
112
impl PredicateValue {
113
696
    pub fn is_number(&self) -> bool {
114
696
        matches!(self, PredicateValue::Number(_))
115
    }
116
1092
    pub fn is_string(&self) -> bool {
117
1092
        matches!(self, PredicateValue::String(_))
118
    }
119

            
120
540
    pub fn is_bytearray(&self) -> bool {
121
540
        matches!(self, PredicateValue::Hex(_)) | matches!(self, PredicateValue::Base64(_))
122
    }
123

            
124
96
    pub fn is_expression(&self) -> bool {
125
96
        matches!(self, PredicateValue::Placeholder(_))
126
    }
127
}
128

            
129
21068
fn equal_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
130
21068
    try_literal("==", reader)?;
131
15604
    let space0 = zero_or_more_spaces(reader)?;
132
15604
    let value = predicate_value(reader)?;
133
15588
    Ok(PredicateFuncValue::Equal { space0, value })
134
}
135

            
136
5464
fn not_equal_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
137
5464
    try_literal("!=", reader)?;
138
248
    let space0 = zero_or_more_spaces(reader)?;
139
248
    let value = predicate_value(reader)?;
140
248
    Ok(PredicateFuncValue::NotEqual { space0, value })
141
}
142

            
143
5136
fn greater_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
144
5136
    try_literal(">", reader)?;
145
308
    let space0 = zero_or_more_spaces(reader)?;
146
308
    let start = reader.cursor();
147
308
    let value = predicate_value(reader)?;
148
308
    if value.is_number() || value.is_string() || value.is_expression() {
149
308
        Ok(PredicateFuncValue::GreaterThan { space0, value })
150
    } else {
151
        Err(ParseError::new(
152
            start.pos,
153
            false,
154
            ParseErrorKind::PredicateValue,
155
        ))
156
    }
157
}
158

            
159
5216
fn greater_or_equal_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
160
5216
    try_literal(">=", reader)?;
161
80
    let space0 = zero_or_more_spaces(reader)?;
162
80
    let start = reader.cursor();
163
80
    let value = predicate_value(reader)?;
164
80
    if value.is_number() || value.is_string() || value.is_expression() {
165
80
        Ok(PredicateFuncValue::GreaterThanOrEqual { space0, value })
166
    } else {
167
        Err(ParseError::new(
168
            start.pos,
169
            false,
170
            ParseErrorKind::PredicateValue,
171
        ))
172
    }
173
}
174

            
175
4708
fn less_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
176
4708
    try_literal("<", reader)?;
177
188
    let space0 = zero_or_more_spaces(reader)?;
178
188
    let start = reader.cursor();
179
188
    let value = predicate_value(reader)?;
180
188
    if value.is_number() || value.is_string() || value.is_expression() {
181
188
        Ok(PredicateFuncValue::LessThan { space0, value })
182
    } else {
183
        Err(ParseError::new(
184
            start.pos,
185
            false,
186
            ParseErrorKind::PredicateValue,
187
        ))
188
    }
189
}
190

            
191
4828
fn less_or_equal_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
192
4828
    try_literal("<=", reader)?;
193
120
    let space0 = zero_or_more_spaces(reader)?;
194
120
    let start = reader.cursor();
195
120
    let value = predicate_value(reader)?;
196
120
    if value.is_number() || value.is_string() || value.is_expression() {
197
120
        Ok(PredicateFuncValue::LessThanOrEqual { space0, value })
198
    } else {
199
        Err(ParseError::new(
200
            start.pos,
201
            false,
202
            ParseErrorKind::PredicateValue,
203
        ))
204
    }
205
}
206

            
207
4520
fn start_with_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
208
4520
    try_literal("startsWith", reader)?;
209
764
    let space0 = one_or_more_spaces(reader)?;
210
764
    let save = reader.cursor();
211
764
    let value = predicate_value(reader)?;
212
764
    if !value.is_string() && !value.is_bytearray() {
213
4
        return Err(ParseError::new(
214
4
            save.pos,
215
4
            false,
216
4
            ParseErrorKind::PredicateValue,
217
4
        ));
218
    }
219
760
    Ok(PredicateFuncValue::StartWith { space0, value })
220
}
221

            
222
3756
fn end_with_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
223
3756
    try_literal("endsWith", reader)?;
224
136
    let space0 = one_or_more_spaces(reader)?;
225
136
    let save = reader.cursor();
226
136
    let value = predicate_value(reader)?;
227
136
    if !value.is_string() && !value.is_bytearray() {
228
        return Err(ParseError::new(
229
            save.pos,
230
            false,
231
            ParseErrorKind::PredicateValue,
232
        ));
233
    }
234
136
    Ok(PredicateFuncValue::EndWith { space0, value })
235
}
236

            
237
3620
fn contain_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
238
3620
    try_literal("contains", reader)?;
239
468
    let space0 = one_or_more_spaces(reader)?;
240
468
    let value = predicate_value(reader)?;
241
468
    Ok(PredicateFuncValue::Contain { space0, value })
242
}
243

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

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

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

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

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

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

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

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

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

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

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

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

            
316
344
fn is_ipv4_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
317
344
    try_literal("isIpv4", reader)?;
318
120
    Ok(PredicateFuncValue::IsIpv4)
319
}
320

            
321
224
fn is_ipv6_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
322
224
    try_literal("isIpv6", reader)?;
323
140
    Ok(PredicateFuncValue::IsIpv6)
324
}
325

            
326
84
fn is_uuid_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
327
84
    try_literal("isUuid", reader)?;
328
80
    Ok(PredicateFuncValue::IsUuid)
329
}
330

            
331
2384
fn is_list_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
332
2384
    try_literal("isList", reader)?;
333
128
    Ok(PredicateFuncValue::IsList)
334
}
335

            
336
2256
fn is_object_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
337
2256
    try_literal("isObject", reader)?;
338
208
    Ok(PredicateFuncValue::IsObject)
339
}
340

            
341
#[cfg(test)]
342
mod tests {
343
    use super::*;
344
    use crate::ast::{
345
        Expr, ExprKind, Float, I64, Number, Placeholder, Template, TemplateElement, Variable,
346
    };
347
    use crate::reader::Pos;
348
    use crate::types::ToSource;
349

            
350
    #[test]
351
    fn test_predicate_not() {
352
        let mut reader = Reader::new("notXX");
353
        assert_eq!(
354
            predicate_not(&mut reader),
355
            (
356
                false,
357
                Whitespace {
358
                    value: String::new(),
359
                    source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
360
                }
361
            )
362
        );
363
        assert_eq!(reader.cursor().pos, Pos { line: 1, column: 1 });
364

            
365
        let mut reader = Reader::new("not XX");
366
        assert_eq!(
367
            predicate_not(&mut reader),
368
            (
369
                true,
370
                Whitespace {
371
                    value: String::from(" "),
372
                    source_info: SourceInfo::new(Pos::new(1, 4), Pos::new(1, 5)),
373
                }
374
            )
375
        );
376
        assert_eq!(reader.cursor().pos, Pos { line: 1, column: 5 });
377
    }
378

            
379
    #[test]
380
    fn test_predicate() {
381
        let mut reader = Reader::new("not == true");
382
        assert_eq!(
383
            predicate(&mut reader).unwrap(),
384
            Predicate {
385
                not: true,
386
                space0: Whitespace {
387
                    value: String::from(" "),
388
                    source_info: SourceInfo::new(Pos::new(1, 4), Pos::new(1, 5)),
389
                },
390
                predicate_func: PredicateFunc {
391
                    source_info: SourceInfo::new(Pos::new(1, 5), Pos::new(1, 12)),
392
                    value: PredicateFuncValue::Equal {
393
                        space0: Whitespace {
394
                            value: String::from(" "),
395
                            source_info: SourceInfo::new(Pos::new(1, 7), Pos::new(1, 8)),
396
                        },
397
                        value: PredicateValue::Bool(true),
398
                    },
399
                },
400
            }
401
        );
402
    }
403

            
404
    #[test]
405
    fn test_predicate_func() {
406
        let mut reader = Reader::new("tata == 1");
407
        let error = predicate_func(&mut reader).err().unwrap();
408
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
409
        assert!(!error.recoverable);
410
        assert_eq!(error.kind, ParseErrorKind::Predicate);
411
    }
412

            
413
    #[test]
414
    fn test_equal_predicate() {
415
        let mut reader = Reader::new("==  true");
416
        assert_eq!(
417
            equal_predicate(&mut reader).unwrap(),
418
            PredicateFuncValue::Equal {
419
                value: PredicateValue::Bool(true),
420
                space0: Whitespace {
421
                    value: String::from("  "),
422
                    source_info: SourceInfo::new(Pos::new(1, 3), Pos::new(1, 5)),
423
                },
424
            }
425
        );
426

            
427
        let mut reader = Reader::new("== 1.1");
428
        assert_eq!(
429
            equal_predicate(&mut reader).unwrap(),
430
            PredicateFuncValue::Equal {
431
                value: PredicateValue::Number(Number::Float(Float::new(1.1, "1.1".to_source()))),
432
                space0: Whitespace {
433
                    value: String::from(" "),
434
                    source_info: SourceInfo::new(Pos::new(1, 3), Pos::new(1, 4)),
435
                },
436
            }
437
        );
438

            
439
        let mut reader = Reader::new("== 2");
440
        assert_eq!(
441
            equal_predicate(&mut reader).unwrap(),
442
            PredicateFuncValue::Equal {
443
                value: PredicateValue::Number(Number::Integer(I64::new(2, "2".to_source()))),
444
                space0: Whitespace {
445
                    value: String::from(" "),
446
                    source_info: SourceInfo::new(Pos::new(1, 3), Pos::new(1, 4)),
447
                },
448
            },
449
        );
450

            
451
        let mut reader = Reader::new("== \"Bob\"");
452
        assert_eq!(
453
            equal_predicate(&mut reader).unwrap(),
454
            PredicateFuncValue::Equal {
455
                value: PredicateValue::String(Template::new(
456
                    Some('"'),
457
                    vec![TemplateElement::String {
458
                        value: "Bob".to_string(),
459
                        source: "Bob".to_source(),
460
                    }],
461
                    SourceInfo::new(Pos::new(1, 4), Pos::new(1, 9)),
462
                )),
463
                space0: Whitespace {
464
                    value: String::from(" "),
465
                    source_info: SourceInfo::new(Pos::new(1, 3), Pos::new(1, 4)),
466
                },
467
            }
468
        );
469
    }
470

            
471
    #[test]
472
    fn test_equal_expression_predicate() {
473
        let mut reader = Reader::new("== {{count}}");
474
        assert_eq!(
475
            equal_predicate(&mut reader).unwrap(),
476
            PredicateFuncValue::Equal {
477
                value: PredicateValue::Placeholder(Placeholder {
478
                    space0: Whitespace {
479
                        value: String::new(),
480
                        source_info: SourceInfo::new(Pos::new(1, 6), Pos::new(1, 6)),
481
                    },
482
                    expr: Expr {
483
                        kind: ExprKind::Variable(Variable {
484
                            name: "count".to_string(),
485
                            source_info: SourceInfo::new(Pos::new(1, 6), Pos::new(1, 11)),
486
                        }),
487
                        source_info: SourceInfo::new(Pos::new(1, 6), Pos::new(1, 11)),
488
                    },
489
                    space1: Whitespace {
490
                        value: String::new(),
491
                        source_info: SourceInfo::new(Pos::new(1, 11), Pos::new(1, 11)),
492
                    },
493
                }),
494
                space0: Whitespace {
495
                    value: String::from(" "),
496
                    source_info: SourceInfo::new(Pos::new(1, 3), Pos::new(1, 4)),
497
                },
498
            }
499
        );
500
    }
501

            
502
    #[test]
503
    fn test_start_with_predicate() {
504
        let mut reader = Reader::new("startsWith 2");
505
        let error = start_with_predicate(&mut reader).err().unwrap();
506
        assert_eq!(
507
            error.pos,
508
            Pos {
509
                line: 1,
510
                column: 12,
511
            }
512
        );
513
        assert!(!error.recoverable);
514
        assert_eq!(error.kind, ParseErrorKind::PredicateValue);
515
    }
516

            
517
    #[test]
518
    fn test_date_predicate() {
519
        let mut reader = Reader::new("isDate");
520
        let result = date_predicate(&mut reader);
521
        assert_eq!(result.unwrap(), PredicateFuncValue::IsDate);
522
    }
523
}