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

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

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

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

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

            
115
325
    pub fn is_bytearray(&self) -> bool {
116
325
        matches!(self, PredicateValue::Hex(_)) | matches!(self, PredicateValue::Base64(_))
117
    }
118

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

            
124
18410
fn equal_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
125
18410
    try_literal("==", reader)?;
126
13790
    let space0 = zero_or_more_spaces(reader)?;
127
13790
    let value = predicate_value(reader)?;
128
13770
    Ok(PredicateFuncValue::Equal { space0, value })
129
}
130

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

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

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

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

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

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

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

            
232
2985
fn contain_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
233
2985
    try_literal("contains", reader)?;
234
545
    let space0 = one_or_more_spaces(reader)?;
235
545
    let value = predicate_value(reader)?;
236
545
    Ok(PredicateFuncValue::Contain { space0, value })
237
}
238

            
239
2440
fn include_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
240
2440
    try_literal("includes", reader)?;
241
15
    let space0 = one_or_more_spaces(reader)?;
242
15
    let value = predicate_value(reader)?;
243
15
    Ok(PredicateFuncValue::Include { space0, value })
244
}
245

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

            
261
2035
fn integer_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
262
2035
    try_literal("isInteger", reader)?;
263
70
    Ok(PredicateFuncValue::IsInteger)
264
}
265

            
266
1965
fn float_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
267
1965
    try_literal("isFloat", reader)?;
268
95
    Ok(PredicateFuncValue::IsFloat)
269
}
270

            
271
1870
fn boolean_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
272
1870
    try_literal("isBoolean", reader)?;
273
55
    Ok(PredicateFuncValue::IsBoolean)
274
}
275

            
276
1815
fn string_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
277
1815
    try_literal("isString", reader)?;
278
55
    Ok(PredicateFuncValue::IsString)
279
}
280

            
281
1760
fn collection_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
282
1760
    try_literal("isCollection", reader)?;
283
155
    Ok(PredicateFuncValue::IsCollection)
284
}
285

            
286
1605
fn date_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
287
1605
    try_literal("isDate", reader)?;
288
80
    Ok(PredicateFuncValue::IsDate)
289
}
290

            
291
1525
fn iso_date_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
292
1525
    try_literal("isIsoDate", reader)?;
293
155
    Ok(PredicateFuncValue::IsIsoDate)
294
}
295

            
296
1370
fn exist_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
297
1370
    try_literal("exists", reader)?;
298
1055
    Ok(PredicateFuncValue::Exist)
299
}
300

            
301
315
fn is_empty_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
302
315
    try_literal("isEmpty", reader)?;
303
205
    Ok(PredicateFuncValue::IsEmpty)
304
}
305

            
306
110
fn is_number_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
307
110
    try_literal("isNumber", reader)?;
308
75
    Ok(PredicateFuncValue::IsNumber)
309
}
310

            
311
35
fn is_ipv4_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
312
35
    try_literal("isIpv4", reader)?;
313
15
    Ok(PredicateFuncValue::IsIpv4)
314
}
315

            
316
20
fn is_ipv6_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
317
20
    try_literal("isIpv6", reader)?;
318
15
    Ok(PredicateFuncValue::IsIpv6)
319
}
320

            
321
#[cfg(test)]
322
mod tests {
323
    use super::*;
324
    use crate::ast::{
325
        Expr, ExprKind, Float, Number, Placeholder, Template, TemplateElement, Variable, I64,
326
    };
327
    use crate::reader::Pos;
328
    use crate::typing::ToSource;
329

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

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

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

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

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

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

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

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

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

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

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