1
/*
2
 * Hurl (https://hurl.dev)
3
 * Copyright (C) 2025 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
22390
pub fn predicate(reader: &mut Reader) -> ParseResult<Predicate> {
28
22390
    let (not, space0) = predicate_not(reader);
29
22390
    let func = predicate_func(reader)?;
30
22360
    Ok(Predicate {
31
22360
        not,
32
22360
        space0,
33
22360
        predicate_func: func,
34
22360
    })
35
}
36

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

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

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

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

            
118
380
    pub fn is_bytearray(&self) -> bool {
119
380
        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
22390
fn equal_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
128
22390
    try_literal("==", reader)?;
129
16600
    let space0 = zero_or_more_spaces(reader)?;
130
16600
    let value = predicate_value(reader)?;
131
16580
    Ok(PredicateFuncValue::Equal { space0, value })
132
}
133

            
134
5790
fn not_equal_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
135
5790
    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
5450
fn greater_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
142
5450
    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
5540
fn greater_or_equal_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
158
5540
    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
4940
fn less_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
174
4940
    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
5080
fn less_or_equal_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
190
5080
    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
4705
fn start_with_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
206
4705
    try_literal("startsWith", reader)?;
207
615
    let space0 = one_or_more_spaces(reader)?;
208
615
    let save = reader.cursor();
209
615
    let value = predicate_value(reader)?;
210
615
    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
610
    Ok(PredicateFuncValue::StartWith { space0, value })
218
}
219

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

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

            
242
3360
fn include_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
243
3360
    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
3335
fn match_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
250
3335
    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
2855
fn integer_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
265
2855
    try_literal("isInteger", reader)?;
266
75
    Ok(PredicateFuncValue::IsInteger)
267
}
268

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

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

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

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

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

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

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

            
304
695
fn is_empty_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
305
695
    try_literal("isEmpty", reader)?;
306
210
    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
2400
fn is_list_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
330
2400
    try_literal("isList", reader)?;
331
160
    Ok(PredicateFuncValue::IsList)
332
}
333

            
334
2240
fn is_object_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
335
2240
    try_literal("isObject", reader)?;
336
140
    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
}