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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
327
#[cfg(test)]
328
mod tests {
329
    use super::*;
330
    use crate::ast::{
331
        Expr, ExprKind, Float, Number, Placeholder, Template, TemplateElement, Variable, I64,
332
    };
333
    use crate::reader::Pos;
334
    use crate::typing::ToSource;
335

            
336
    #[test]
337
    fn test_predicate_not() {
338
        let mut reader = Reader::new("notXX");
339
        assert_eq!(
340
            predicate_not(&mut reader),
341
            (
342
                false,
343
                Whitespace {
344
                    value: String::new(),
345
                    source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
346
                }
347
            )
348
        );
349
        assert_eq!(reader.cursor().pos, Pos { line: 1, column: 1 });
350

            
351
        let mut reader = Reader::new("not XX");
352
        assert_eq!(
353
            predicate_not(&mut reader),
354
            (
355
                true,
356
                Whitespace {
357
                    value: String::from(" "),
358
                    source_info: SourceInfo::new(Pos::new(1, 4), Pos::new(1, 5)),
359
                }
360
            )
361
        );
362
        assert_eq!(reader.cursor().pos, Pos { line: 1, column: 5 });
363
    }
364

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

            
390
    #[test]
391
    fn test_predicate_func() {
392
        let mut reader = Reader::new("tata == 1");
393
        let error = predicate_func(&mut reader).err().unwrap();
394
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
395
        assert!(!error.recoverable);
396
        assert_eq!(error.kind, ParseErrorKind::Predicate);
397
    }
398

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

            
413
        let mut reader = Reader::new("== 1.1");
414
        assert_eq!(
415
            equal_predicate(&mut reader).unwrap(),
416
            PredicateFuncValue::Equal {
417
                value: PredicateValue::Number(Number::Float(Float::new(1.1, "1.1".to_source()))),
418
                space0: Whitespace {
419
                    value: String::from(" "),
420
                    source_info: SourceInfo::new(Pos::new(1, 3), Pos::new(1, 4)),
421
                },
422
            }
423
        );
424

            
425
        let mut reader = Reader::new("== 2");
426
        assert_eq!(
427
            equal_predicate(&mut reader).unwrap(),
428
            PredicateFuncValue::Equal {
429
                value: PredicateValue::Number(Number::Integer(I64::new(2, "2".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("== \"Bob\"");
438
        assert_eq!(
439
            equal_predicate(&mut reader).unwrap(),
440
            PredicateFuncValue::Equal {
441
                value: PredicateValue::String(Template::new(
442
                    Some('"'),
443
                    vec![TemplateElement::String {
444
                        value: "Bob".to_string(),
445
                        source: "Bob".to_source(),
446
                    }],
447
                    SourceInfo::new(Pos::new(1, 4), Pos::new(1, 9)),
448
                )),
449
                space0: Whitespace {
450
                    value: String::from(" "),
451
                    source_info: SourceInfo::new(Pos::new(1, 3), Pos::new(1, 4)),
452
                },
453
            }
454
        );
455
    }
456

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

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

            
503
    #[test]
504
    fn test_date_predicate() {
505
        let mut reader = Reader::new("isDate");
506
        let result = date_predicate(&mut reader);
507
        assert_eq!(result.unwrap(), PredicateFuncValue::IsDate);
508
    }
509
}