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::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
23110
pub fn predicate(reader: &mut Reader) -> ParseResult<Predicate> {
28
23110
    let (not, space0) = predicate_not(reader);
29
23110
    let func = predicate_func(reader)?;
30
23080
    Ok(Predicate {
31
23080
        not,
32
23080
        space0,
33
23080
        predicate_func: func,
34
23080
    })
35
}
36

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

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

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

            
110
impl PredicateValue {
111
835
    pub fn is_number(&self) -> bool {
112
835
        matches!(self, PredicateValue::Number(_))
113
    }
114
1205
    pub fn is_string(&self) -> bool {
115
1205
        matches!(self, PredicateValue::String(_))
116
    }
117

            
118
565
    pub fn is_bytearray(&self) -> bool {
119
565
        matches!(self, PredicateValue::Hex(_)) | matches!(self, PredicateValue::Base64(_))
120
    }
121

            
122
120
    pub fn is_expression(&self) -> bool {
123
120
        matches!(self, PredicateValue::Placeholder(_))
124
    }
125
}
126

            
127
23110
fn equal_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
128
23110
    try_literal("==", reader)?;
129
17005
    let space0 = zero_or_more_spaces(reader)?;
130
17005
    let value = predicate_value(reader)?;
131
16985
    Ok(PredicateFuncValue::Equal { space0, value })
132
}
133

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

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

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

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

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

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

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

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

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

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

            
264
2980
fn integer_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
265
2980
    try_literal("isInteger", reader)?;
266
75
    Ok(PredicateFuncValue::IsInteger)
267
}
268

            
269
2905
fn float_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
270
2905
    try_literal("isFloat", reader)?;
271
100
    Ok(PredicateFuncValue::IsFloat)
272
}
273

            
274
2805
fn boolean_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
275
2805
    try_literal("isBoolean", reader)?;
276
60
    Ok(PredicateFuncValue::IsBoolean)
277
}
278

            
279
2745
fn string_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
280
2745
    try_literal("isString", reader)?;
281
60
    Ok(PredicateFuncValue::IsString)
282
}
283

            
284
2685
fn collection_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
285
2685
    try_literal("isCollection", reader)?;
286
160
    Ok(PredicateFuncValue::IsCollection)
287
}
288

            
289
2195
fn date_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
290
2195
    try_literal("isDate", reader)?;
291
95
    Ok(PredicateFuncValue::IsDate)
292
}
293

            
294
2100
fn iso_date_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
295
2100
    try_literal("isIsoDate", reader)?;
296
160
    Ok(PredicateFuncValue::IsIsoDate)
297
}
298

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

            
304
680
fn is_empty_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
305
680
    try_literal("isEmpty", reader)?;
306
195
    Ok(PredicateFuncValue::IsEmpty)
307
}
308

            
309
485
fn is_number_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
310
485
    try_literal("isNumber", reader)?;
311
80
    Ok(PredicateFuncValue::IsNumber)
312
}
313

            
314
405
fn is_ipv4_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
315
405
    try_literal("isIpv4", reader)?;
316
150
    Ok(PredicateFuncValue::IsIpv4)
317
}
318

            
319
255
fn is_ipv6_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
320
255
    try_literal("isIpv6", reader)?;
321
150
    Ok(PredicateFuncValue::IsIpv6)
322
}
323

            
324
105
fn is_uuid_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
325
105
    try_literal("isUuid", reader)?;
326
100
    Ok(PredicateFuncValue::IsUuid)
327
}
328

            
329
2525
fn is_list_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
330
2525
    try_literal("isList", reader)?;
331
160
    Ok(PredicateFuncValue::IsList)
332
}
333

            
334
2365
fn is_object_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
335
2365
    try_literal("isObject", reader)?;
336
170
    Ok(PredicateFuncValue::IsObject)
337
}
338

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

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

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

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

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

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

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

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

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

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

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

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