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::VersionValue::VersionAny;
19
use crate::ast::{
20
    Body, Entry, HurlFile, Method, Request, Response, Section, SourceInfo, Status, StatusValue,
21
    Version, VersionValue,
22
};
23
use crate::combinator::{optional, zero_or_more};
24
use crate::reader::Reader;
25

            
26
use super::ParseResult;
27
use super::bytes::bytes;
28
use super::error::{ParseError, ParseErrorKind};
29
use super::primitives::{
30
    eof, key_value, line_terminator, one_or_more_spaces, optional_line_terminators, try_literal,
31
    zero_or_more_spaces,
32
};
33
use super::sections::{request_sections, response_sections};
34
use super::string::unquoted_template;
35

            
36
4160
pub fn hurl_file(reader: &mut Reader) -> ParseResult<HurlFile> {
37
4160
    let entries = zero_or_more(entry, reader)?;
38
3948
    let line_terminators = optional_line_terminators(reader)?;
39
3948
    eof(reader)?;
40
3948
    Ok(HurlFile {
41
3948
        entries,
42
3948
        line_terminators,
43
3948
    })
44
}
45

            
46
14992
fn entry(reader: &mut Reader) -> ParseResult<Entry> {
47
14992
    let req = request(reader)?;
48
13896
    let resp = optional(response, reader)?;
49
13836
    Ok(Entry {
50
13836
        request: req,
51
13836
        response: resp,
52
13836
    })
53
}
54

            
55
14992
fn request(reader: &mut Reader) -> ParseResult<Request> {
56
14992
    let start = reader.cursor();
57
14992
    let line_terminators = optional_line_terminators(reader)?;
58
14992
    let space0 = zero_or_more_spaces(reader)?;
59
14992
    let method = method(reader)?;
60
14020
    let space1 = one_or_more_spaces(reader)?;
61
14004
    let url = unquoted_template(reader)?;
62
14000
    let line_terminator0 = line_terminator(reader)?;
63
14000
    let headers = zero_or_more(key_value, reader)?;
64
14000
    let sections = request_sections(reader)?;
65
13956
    let body = optional(body, reader)?;
66
13900
    let source_info = SourceInfo::new(start.pos, reader.cursor().pos);
67

            
68
13900
    check_duplicated_sections(&sections)?;
69

            
70
13896
    Ok(Request {
71
13896
        line_terminators,
72
13896
        space0,
73
13896
        method,
74
13896
        space1,
75
13896
        url,
76
13896
        line_terminator0,
77
13896
        headers,
78
13896
        sections,
79
13896
        body,
80
13896
        source_info,
81
13896
    })
82
}
83

            
84
13896
fn response(reader: &mut Reader) -> ParseResult<Response> {
85
13896
    let start = reader.cursor();
86
13896
    let line_terminators = optional_line_terminators(reader)?;
87
13896
    let space0 = zero_or_more_spaces(reader)?;
88
13896
    let version = version(reader)?;
89
12724
    let space1 = one_or_more_spaces(reader)?;
90
12724
    let status = status(reader)?;
91
12720
    let line_terminator0 = line_terminator(reader)?;
92
12720
    let headers = zero_or_more(key_value, reader)?;
93
12720
    let sections = response_sections(reader)?;
94
12672
    let body = optional(body, reader)?;
95
12672
    let source_info = SourceInfo::new(start.pos, reader.cursor().pos);
96

            
97
12672
    check_duplicated_sections(&sections)?;
98

            
99
12668
    Ok(Response {
100
12668
        line_terminators,
101
12668
        space0,
102
12668
        version,
103
12668
        space1,
104
12668
        status,
105
12668
        line_terminator0,
106
12668
        headers,
107
12668
        sections,
108
12668
        body,
109
12668
        source_info,
110
12668
    })
111
}
112

            
113
26572
fn check_duplicated_sections(sections: &[Section]) -> Result<(), ParseError> {
114
26572
    let mut section_names = vec![];
115
26572
    for section in sections {
116
10848
        if section_names.contains(&section.identifier()) {
117
8
            return Err(ParseError::new(
118
8
                section.source_info.start,
119
8
                false,
120
8
                ParseErrorKind::DuplicateSection,
121
8
            ));
122
10840
        } else {
123
10840
            section_names.push(section.identifier());
124
        }
125
    }
126
26564
    Ok(())
127
}
128

            
129
14992
fn method(reader: &mut Reader) -> ParseResult<Method> {
130
14992
    if reader.is_eof() {
131
944
        let kind = ParseErrorKind::Method {
132
944
            name: "<EOF>".to_string(),
133
944
        };
134
944
        return Err(ParseError::new(reader.cursor().pos, true, kind));
135
    }
136
14048
    let start = reader.cursor();
137
58824
    let name = reader.read_while(|c| c.is_ascii_alphabetic());
138
14048
    if name.is_empty() || name.to_uppercase() != name {
139
28
        let kind = ParseErrorKind::Method { name };
140
28
        Err(ParseError::new(start.pos, false, kind))
141
    } else {
142
14020
        Ok(Method::new(&name))
143
    }
144
}
145

            
146
13896
fn version(reader: &mut Reader) -> ParseResult<Version> {
147
13896
    let start = reader.cursor();
148
13896
    try_literal("HTTP", reader)?;
149

            
150
12728
    let next_c = reader.peek();
151
12728
    match next_c {
152
        Some('/') => {
153
208
            let available_version = [
154
208
                ("/1.0", VersionValue::Version1),
155
208
                ("/1.1", VersionValue::Version11),
156
208
                ("/2", VersionValue::Version2),
157
208
                ("/3", VersionValue::Version3),
158
208
            ];
159
512
            for (s, value) in available_version.iter() {
160
512
                if try_literal(s, reader).is_ok() {
161
204
                    return Ok(Version {
162
204
                        value: *value,
163
204
                        source_info: SourceInfo::new(start.pos, reader.cursor().pos),
164
204
                    });
165
                }
166
            }
167
4
            Err(ParseError::new(start.pos, false, ParseErrorKind::Version))
168
        }
169
12520
        Some(' ') | Some('\t') => Ok(Version {
170
12520
            value: VersionAny,
171
12520
            source_info: SourceInfo::new(start.pos, reader.cursor().pos),
172
12520
        }),
173
        _ => Err(ParseError::new(start.pos, false, ParseErrorKind::Version)),
174
    }
175
}
176

            
177
12724
fn status(reader: &mut Reader) -> ParseResult<Status> {
178
12724
    let start = reader.cursor();
179
12724
    let value = match try_literal("*", reader) {
180
224
        Ok(_) => StatusValue::Any,
181
        Err(_) => {
182
12500
            if reader.is_eof() {
183
                let kind = ParseErrorKind::Status;
184
                return Err(ParseError::new(start.pos, false, kind));
185
            }
186
49984
            let s = reader.read_while(|c| c.is_ascii_digit());
187
12500
            if s.is_empty() {
188
4
                let kind = ParseErrorKind::Status;
189
4
                return Err(ParseError::new(start.pos, false, kind));
190
            }
191
12496
            match s.to_string().parse() {
192
12496
                Ok(value) => StatusValue::Specific(value),
193
                Err(_) => {
194
                    let kind = ParseErrorKind::Status;
195
                    return Err(ParseError::new(start.pos, false, kind));
196
                }
197
            }
198
        }
199
    };
200
12720
    let end = reader.cursor();
201
12720
    Ok(Status {
202
12720
        value,
203
12720
        source_info: SourceInfo::new(start.pos, end.pos),
204
12720
    })
205
}
206

            
207
26628
fn body(reader: &mut Reader) -> ParseResult<Body> {
208
    //  let start = reader.state.clone();
209
26628
    let line_terminators = optional_line_terminators(reader)?;
210
26628
    let space0 = zero_or_more_spaces(reader)?;
211
26628
    let value = bytes(reader)?;
212
3712
    let line_terminator0 = line_terminator(reader)?;
213
3712
    Ok(Body {
214
3712
        line_terminators,
215
3712
        space0,
216
3712
        value,
217
3712
        line_terminator0,
218
3712
    })
219
}
220

            
221
#[cfg(test)]
222
mod tests {
223
    use super::*;
224
    use crate::ast::{
225
        Bytes, Comment, JsonListElement, JsonValue, JsonValueKind, LineTerminator, MultilineString,
226
        MultilineStringKind, Template, TemplateElement, Whitespace,
227
    };
228
    use crate::reader::{CharPos, Pos};
229
    use crate::types::ToSource;
230

            
231
    #[test]
232
    fn test_hurl_file() {
233
        let mut reader = Reader::new("GET http://google.fr");
234
        let hurl_file = hurl_file(&mut reader).unwrap();
235
        assert_eq!(hurl_file.entries.len(), 1);
236
    }
237

            
238
    #[test]
239
    fn test_entry() {
240
        let mut reader = Reader::new("GET http://google.fr");
241
        let e = entry(&mut reader).unwrap();
242
        assert_eq!(e.request.method, Method::new("GET"));
243
        assert_eq!(reader.cursor().index, CharPos(20));
244
    }
245

            
246
    #[test]
247
    fn test_several_entry() {
248
        let mut reader = Reader::new("GET http://google.fr\nGET http://google.fr");
249

            
250
        let e = entry(&mut reader).unwrap();
251
        assert_eq!(e.request.method, Method::new("GET"));
252
        assert_eq!(reader.cursor().index, CharPos(21));
253
        assert_eq!(reader.cursor().pos.line, 2);
254

            
255
        let e = entry(&mut reader).unwrap();
256
        assert_eq!(e.request.method, Method::new("GET"));
257
        assert_eq!(reader.cursor().index, CharPos(41));
258
        assert_eq!(reader.cursor().pos.line, 2);
259

            
260
        let mut reader =
261
            Reader::new("GET http://google.fr # comment1\nGET http://google.fr # comment2");
262

            
263
        let e = entry(&mut reader).unwrap();
264
        assert_eq!(e.request.method, Method::new("GET"));
265
        assert_eq!(reader.cursor().index, CharPos(32));
266
        assert_eq!(reader.cursor().pos.line, 2);
267

            
268
        let e = entry(&mut reader).unwrap();
269
        assert_eq!(e.request.method, Method::new("GET"));
270
        assert_eq!(reader.cursor().index, CharPos(63));
271
        assert_eq!(reader.cursor().pos.line, 2);
272
    }
273

            
274
    #[test]
275
    fn test_entry_with_response() {
276
        let mut reader = Reader::new("GET http://google.fr\nHTTP/1.1 200");
277
        let e = entry(&mut reader).unwrap();
278
        assert_eq!(e.request.method, Method::new("GET"));
279
        assert_eq!(e.response.unwrap().status.value, StatusValue::Specific(200));
280
    }
281

            
282
    #[test]
283
    fn test_request() {
284
        let mut reader = Reader::new("GET http://google.fr");
285
        let default_request = Request {
286
            line_terminators: vec![],
287
            space0: Whitespace {
288
                value: String::new(),
289
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
290
            },
291
            method: Method::new("GET"),
292
            space1: Whitespace {
293
                value: " ".to_string(),
294
                source_info: SourceInfo::new(Pos::new(1, 4), Pos::new(1, 5)),
295
            },
296
            url: Template::new(
297
                None,
298
                vec![TemplateElement::String {
299
                    value: "http://google.fr".to_string(),
300
                    source: "http://google.fr".to_source(),
301
                }],
302
                SourceInfo::new(Pos::new(1, 5), Pos::new(1, 21)),
303
            ),
304
            line_terminator0: LineTerminator {
305
                space0: Whitespace {
306
                    value: String::new(),
307
                    source_info: SourceInfo::new(Pos::new(1, 21), Pos::new(1, 21)),
308
                },
309
                comment: None,
310
                newline: Whitespace {
311
                    value: String::new(),
312
                    source_info: SourceInfo::new(Pos::new(1, 21), Pos::new(1, 21)),
313
                },
314
            },
315
            headers: vec![],
316
            sections: vec![],
317
            body: None,
318
            source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 21)),
319
        };
320
        assert_eq!(request(&mut reader).unwrap(), default_request);
321
        assert_eq!(reader.cursor().index, CharPos(20));
322

            
323
        let mut reader = Reader::new("GET  http://google.fr # comment");
324
        let default_request = Request {
325
            line_terminators: vec![],
326
            space0: Whitespace {
327
                value: String::new(),
328
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
329
            },
330
            method: Method::new("GET"),
331
            space1: Whitespace {
332
                value: "  ".to_string(),
333
                source_info: SourceInfo::new(Pos::new(1, 4), Pos::new(1, 6)),
334
            },
335
            url: Template::new(
336
                None,
337
                vec![TemplateElement::String {
338
                    value: "http://google.fr".to_string(),
339
                    source: "http://google.fr".to_source(),
340
                }],
341
                SourceInfo::new(Pos::new(1, 6), Pos::new(1, 22)),
342
            ),
343
            line_terminator0: LineTerminator {
344
                space0: Whitespace {
345
                    value: " ".to_string(),
346
                    source_info: SourceInfo::new(Pos::new(1, 22), Pos::new(1, 23)),
347
                },
348
                comment: Some(Comment {
349
                    value: " comment".to_string(),
350
                    source_info: SourceInfo::new(Pos::new(1, 24), Pos::new(1, 32)),
351
                }),
352
                newline: Whitespace {
353
                    value: String::new(),
354
                    source_info: SourceInfo::new(Pos::new(1, 32), Pos::new(1, 32)),
355
                },
356
            },
357
            headers: vec![],
358
            sections: vec![],
359
            body: None,
360
            source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 32)),
361
        };
362
        assert_eq!(request(&mut reader).unwrap(), default_request);
363
        assert_eq!(reader.cursor().index, CharPos(31));
364

            
365
        let mut reader = Reader::new("GET http://google.fr\nGET http://google.fr");
366
        let r = request(&mut reader).unwrap();
367
        assert_eq!(r.method, Method::new("GET"));
368
        assert_eq!(reader.cursor().index, CharPos(21));
369
        let r = request(&mut reader).unwrap();
370
        assert_eq!(r.method, Method::new("GET"));
371
    }
372

            
373
    #[test]
374
    fn test_request_multilines() {
375
        // GET http://google.fr
376
        // ```
377
        // Hello World!
378
        // ```
379
        let mut reader = Reader::new("GET http://google.fr\n```\nHello World!\n```");
380
        let req = request(&mut reader).unwrap();
381
        assert_eq!(
382
            req.body.unwrap(),
383
            Body {
384
                line_terminators: vec![],
385
                space0: Whitespace {
386
                    value: String::new(),
387
                    source_info: SourceInfo::new(Pos::new(2, 1), Pos::new(2, 1)),
388
                },
389
                value: Bytes::MultilineString(MultilineString {
390
                    space: Whitespace {
391
                        value: String::new(),
392
                        source_info: SourceInfo::new(Pos::new(2, 4), Pos::new(2, 4)),
393
                    },
394
                    newline: Whitespace {
395
                        source_info: SourceInfo::new(Pos::new(2, 4), Pos::new(3, 1)),
396
                        value: "\n".to_string(),
397
                    },
398
                    kind: MultilineStringKind::Text(Template::new(
399
                        None,
400
                        vec![TemplateElement::String {
401
                            value: "Hello World!\n".to_string(),
402
                            source: "Hello World!\n".to_source(),
403
                        }],
404
                        SourceInfo::new(Pos::new(3, 1), Pos::new(4, 1)),
405
                    )),
406
                }),
407
                line_terminator0: LineTerminator {
408
                    space0: Whitespace {
409
                        value: String::new(),
410
                        source_info: SourceInfo::new(Pos::new(4, 4), Pos::new(4, 4)),
411
                    },
412
                    comment: None,
413
                    newline: Whitespace {
414
                        value: String::new(),
415
                        source_info: SourceInfo::new(Pos::new(4, 4), Pos::new(4, 4)),
416
                    },
417
                },
418
            }
419
        );
420
    }
421

            
422
    #[test]
423
    fn test_request_post_json() {
424
        let mut reader = Reader::new("POST http://localhost:8000/post-json-array\n[1,2,3]");
425
        let r = request(&mut reader).unwrap();
426
        assert_eq!(r.method, Method::new("POST"));
427
        assert_eq!(
428
            r.body.unwrap().value,
429
            Bytes::Json(JsonValue::new(
430
                SourceInfo::new(Pos::new(2, 1), Pos::new(2, 8)),
431
                JsonValueKind::List {
432
                    space0: String::new(),
433
                    elements: vec![
434
                        JsonListElement {
435
                            space0: String::new(),
436
                            value: JsonValue::new(
437
                                SourceInfo::new(Pos::new(2, 2), Pos::new(2, 3)),
438
                                JsonValueKind::Number("1".to_string()),
439
                            ),
440
                            space1: String::new(),
441
                        },
442
                        JsonListElement {
443
                            space0: String::new(),
444
                            value: JsonValue::new(
445
                                SourceInfo::new(Pos::new(2, 4), Pos::new(2, 5)),
446
                                JsonValueKind::Number("2".to_string()),
447
                            ),
448
                            space1: String::new(),
449
                        },
450
                        JsonListElement {
451
                            space0: String::new(),
452
                            value: JsonValue::new(
453
                                SourceInfo::new(Pos::new(2, 6), Pos::new(2, 7)),
454
                                JsonValueKind::Number("3".to_string()),
455
                            ),
456
                            space1: String::new(),
457
                        },
458
                    ],
459
                },
460
            ))
461
        );
462

            
463
        let mut reader = Reader::new("POST http://localhost:8000/post-json-string\n\"Hello\"");
464
        let r = request(&mut reader).unwrap();
465
        assert_eq!(r.method, Method::new("POST"));
466
        assert_eq!(
467
            r.body.unwrap().value,
468
            Bytes::Json(JsonValue::new(
469
                SourceInfo::new(Pos::new(2, 1), Pos::new(2, 8)),
470
                JsonValueKind::String(Template::new(
471
                    Some('"'),
472
                    vec![TemplateElement::String {
473
                        value: "Hello".to_string(),
474
                        source: "Hello".to_source(),
475
                    }],
476
                    SourceInfo::new(Pos::new(2, 2), Pos::new(2, 7)),
477
                )),
478
            ))
479
        );
480

            
481
        let mut reader = Reader::new("POST http://localhost:8000/post-json-number\n100");
482
        let r = request(&mut reader).unwrap();
483
        assert_eq!(r.method, Method::new("POST"));
484
        assert_eq!(
485
            r.body.unwrap().value,
486
            Bytes::Json(JsonValue::new(
487
                SourceInfo::new(Pos::new(2, 1), Pos::new(2, 4)),
488
                JsonValueKind::Number("100".to_string()),
489
            ))
490
        );
491
    }
492

            
493
    #[test]
494
    fn test_request_error() {
495
        let mut reader = Reader::new("xxx");
496
        let error = request(&mut reader).err().unwrap();
497
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
498
    }
499

            
500
    #[test]
501
    fn test_response() {
502
        let mut reader = Reader::new("HTTP/1.1 200");
503
        //println!("{:?}", response(&mut reader));
504
        let r = response(&mut reader).unwrap();
505

            
506
        assert_eq!(r.version.value, VersionValue::Version11);
507
        assert_eq!(r.status.value, StatusValue::Specific(200));
508
    }
509

            
510
    #[test]
511
    fn test_method() {
512
        let mut reader = Reader::new("xxx ");
513
        let error = method(&mut reader).err().unwrap();
514
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
515
        assert_eq!(reader.cursor().index, CharPos(3));
516

            
517
        let mut reader = Reader::new("");
518
        let error = method(&mut reader).err().unwrap();
519
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
520
        assert_eq!(reader.cursor().index, CharPos(0));
521

            
522
        let mut reader = Reader::new("GET ");
523
        assert_eq!(method(&mut reader).unwrap(), Method::new("GET"));
524
        assert_eq!(reader.cursor().index, CharPos(3));
525

            
526
        let mut reader = Reader::new("CUSTOM");
527
        assert_eq!(method(&mut reader).unwrap(), Method::new("CUSTOM"));
528
        assert_eq!(reader.cursor().index, CharPos(6));
529
    }
530

            
531
    #[test]
532
    fn test_version() {
533
        let mut reader = Reader::new("HTTP 200");
534
        assert_eq!(version(&mut reader).unwrap().value, VersionAny);
535
        assert_eq!(reader.cursor().index, CharPos(4));
536

            
537
        let mut reader = Reader::new("HTTP\t200");
538
        assert_eq!(version(&mut reader).unwrap().value, VersionAny);
539
        assert_eq!(reader.cursor().index, CharPos(4));
540

            
541
        let mut reader = Reader::new("HTTP/1.1 200");
542
        assert_eq!(version(&mut reader).unwrap().value, VersionValue::Version11);
543

            
544
        let mut reader = Reader::new("HTTP/1. 200");
545
        let error = version(&mut reader).err().unwrap();
546
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
547
    }
548

            
549
    #[test]
550
    fn test_status() {
551
        let mut reader = Reader::new("*");
552
        let s = status(&mut reader).unwrap();
553
        assert_eq!(s.value, StatusValue::Any);
554

            
555
        let mut reader = Reader::new("200");
556
        let s = status(&mut reader).unwrap();
557
        assert_eq!(s.value, StatusValue::Specific(200));
558

            
559
        let mut reader = Reader::new("xxx");
560
        let result = status(&mut reader);
561
        assert!(result.is_err());
562
    }
563

            
564
    #[test]
565
    fn test_body_json() {
566
        let mut reader = Reader::new("[1,2,3] ");
567
        let b = body(&mut reader).unwrap();
568
        assert_eq!(b.line_terminators.len(), 0);
569
        assert_eq!(
570
            b.value,
571
            Bytes::Json(JsonValue::new(
572
                SourceInfo::new(Pos::new(1, 1), Pos::new(1, 8)),
573
                JsonValueKind::List {
574
                    space0: String::new(),
575
                    elements: vec![
576
                        JsonListElement {
577
                            space0: String::new(),
578
                            value: JsonValue::new(
579
                                SourceInfo::new(Pos::new(1, 2), Pos::new(1, 3)),
580
                                JsonValueKind::Number("1".to_string()),
581
                            ),
582
                            space1: String::new(),
583
                        },
584
                        JsonListElement {
585
                            space0: String::new(),
586
                            value: JsonValue::new(
587
                                SourceInfo::new(Pos::new(1, 4), Pos::new(1, 5)),
588
                                JsonValueKind::Number("2".to_string()),
589
                            ),
590
                            space1: String::new(),
591
                        },
592
                        JsonListElement {
593
                            space0: String::new(),
594
                            value: JsonValue::new(
595
                                SourceInfo::new(Pos::new(1, 6), Pos::new(1, 7)),
596
                                JsonValueKind::Number("3".to_string()),
597
                            ),
598
                            space1: String::new(),
599
                        },
600
                    ],
601
                },
602
            ))
603
        );
604
        assert_eq!(reader.cursor().index, CharPos(8));
605

            
606
        let mut reader = Reader::new("{}");
607
        let b = body(&mut reader).unwrap();
608
        assert_eq!(b.line_terminators.len(), 0);
609
        assert_eq!(
610
            b.value,
611
            Bytes::Json(JsonValue::new(
612
                SourceInfo::new(Pos::new(1, 1), Pos::new(1, 3)),
613
                JsonValueKind::Object {
614
                    space0: String::new(),
615
                    elements: vec![],
616
                },
617
            ))
618
        );
619
        assert_eq!(reader.cursor().index, CharPos(2));
620

            
621
        let mut reader = Reader::new("# comment\n {} # comment\nxxx");
622
        let b = body(&mut reader).unwrap();
623
        assert_eq!(b.line_terminators.len(), 1);
624
        assert_eq!(
625
            b.value,
626
            Bytes::Json(JsonValue::new(
627
                SourceInfo::new(Pos::new(2, 2), Pos::new(2, 4)),
628
                JsonValueKind::Object {
629
                    space0: String::new(),
630
                    elements: vec![],
631
                },
632
            ))
633
        );
634
        assert_eq!(reader.cursor().index, CharPos(24));
635

            
636
        let mut reader = Reader::new("{x");
637
        let error = body(&mut reader).err().unwrap();
638
        assert_eq!(error.pos, Pos { line: 1, column: 2 });
639
        assert!(!error.recoverable);
640
    }
641
}