ion_c_sys/
reader.rs

1// Copyright Amazon.com, Inc. or its affiliates.
2
3//! Provides higher-level APIs for Ion C's `hREADER`.
4
5use ion_c_sys_macros::position_error;
6use std::convert::{TryFrom, TryInto};
7use std::marker::PhantomData;
8use std::ops::{Deref, DerefMut};
9use std::os::raw::c_int;
10use std::ptr;
11
12use crate::result::*;
13use crate::string::*;
14use crate::*;
15
16/// The reading API for Ion C.
17///
18/// See also [`IonCReaderHandle`](./struct.IonCReaderHandle.html).
19///
20/// ## Usage
21/// ```
22/// # use ion_c_sys::*;
23/// # use ion_c_sys::reader::*;
24/// # use ion_c_sys::result::*;
25/// # use std::convert::*;
26/// # use std::ptr;
27/// # fn main() -> IonCResult<()> {
28/// let mut reader = IonCReaderHandle::try_from(b"\xE0\x01\x00\xEA\x85hello".as_ref())?;
29/// let tid = reader.next()?;
30/// assert_eq!(ION_TYPE_STRING, tid);
31/// // reader_handle implements Drop, so we're good to go!
32/// # Ok(())
33/// # }
34/// ```
35pub trait IonCReader {
36    /// Advances the reader to the next value and returns the type.
37    fn next(&mut self) -> IonCResult<ION_TYPE>;
38
39    /// Returns the type of the current position.
40    ///
41    /// ## Usage
42    /// ```
43    /// # use std::convert::*;
44    /// # use ion_c_sys::*;
45    /// # use ion_c_sys::reader::*;
46    /// # use ion_c_sys::result::*;
47    /// # fn main() -> IonCResult<()> {
48    /// let mut reader = IonCReaderHandle::try_from("'''hello!'''")?;
49    /// assert_eq!(ION_TYPE_NONE, reader.get_type()?);
50    /// assert_eq!(ION_TYPE_STRING, reader.next()?);
51    /// assert_eq!(ION_TYPE_STRING, reader.get_type()?);
52    /// assert_eq!(ION_TYPE_EOF, reader.next()?);
53    /// assert_eq!(ION_TYPE_EOF, reader.get_type()?);
54    /// # Ok(())
55    /// # }
56    /// ```
57    fn get_type(&self) -> IonCResult<ION_TYPE>;
58
59    /// Steps in to the current container.
60    fn step_in(&mut self) -> IonCResult<()>;
61
62    /// Steps out of the current container.
63    fn step_out(&mut self) -> IonCResult<()>;
64
65    /// Returns the current container depth.
66    ///
67    /// ## Usage
68    /// ```
69    /// # use std::convert::*;
70    /// # use ion_c_sys::*;
71    /// # use ion_c_sys::reader::*;
72    /// # use ion_c_sys::result::*;
73    /// # fn main() -> IonCResult<()> {
74    /// let mut reader = IonCReaderHandle::try_from("[[]]")?;
75    /// assert_eq!(ION_TYPE_LIST, reader.next()?);
76    /// reader.step_in()?;
77    /// assert_eq!(1, reader.depth()?);
78    /// assert_eq!(ION_TYPE_LIST, reader.next()?);
79    /// reader.step_in()?;
80    /// assert_eq!(2, reader.depth()?);
81    /// # Ok(())
82    /// # }
83    /// ```
84    fn depth(&self) -> IonCResult<i32>;
85
86    /// Returns if the reader is positioned on a `null` value.
87    ///
88    /// ## Usage
89    /// ```
90    /// # use std::convert::*;
91    /// # use ion_c_sys::*;
92    /// # use ion_c_sys::reader::*;
93    /// # use ion_c_sys::result::*;
94    /// # fn main() -> IonCResult<()> {
95    /// let mut reader = IonCReaderHandle::try_from("null.int 4")?;
96    /// assert_eq!(ION_TYPE_INT, reader.next()?);
97    /// assert!(reader.is_null()?);
98    /// assert_eq!(ION_TYPE_INT, reader.next()?);
99    /// assert!(!reader.is_null()?);
100    /// # Ok(())
101    /// # }
102    /// ```
103    fn is_null(&self) -> IonCResult<bool>;
104
105    /// Returns if the reader is positioned within a `struct` value.
106    /// ## Usage
107    /// ```
108    /// # use std::convert::*;
109    /// # use ion_c_sys::*;
110    /// # use ion_c_sys::reader::*;
111    /// # use ion_c_sys::result::*;
112    /// # fn main() -> IonCResult<()> {
113    /// let mut reader = IonCReaderHandle::try_from("{}")?;
114    /// assert_eq!(ION_TYPE_STRUCT, reader.next()?);
115    /// assert!(!reader.is_in_struct()?);
116    /// reader.step_in()?;
117    /// assert!(reader.is_in_struct()?);
118    /// # Ok(())
119    /// # }
120    /// ```
121    fn is_in_struct(&self) -> IonCResult<bool>;
122
123    /// Returns the field name if the reader positioned within a structure.
124    ///
125    /// ## Usage
126    /// ```
127    /// # use std::convert::*;
128    /// # use ion_c_sys::*;
129    /// # use ion_c_sys::reader::*;
130    /// # use ion_c_sys::result::*;
131    /// # use ion_c_sys::string::*;
132    /// # fn main() -> IonCResult<()> {
133    /// let mut reader = IonCReaderHandle::try_from("{a:5}")?;
134    /// assert_eq!(ION_TYPE_STRUCT, reader.next()?);
135    /// reader.step_in()?;
136    /// assert_eq!(ION_TYPE_INT, reader.next()?);
137    /// assert_eq!("a", reader.get_field_name()?.as_str());
138    /// # Ok(())
139    /// # }
140    /// ```
141    fn get_field_name(&mut self) -> IonCResult<StrSliceRef>;
142
143    /// Retrieves the annotations associated with the current value.
144    ///
145    /// Note that this allocates a vector on the heap for the `IonCStringRef` instances.
146    /// If this is not desired, use the low-level annotation functions.
147    ///
148    /// ## Usage
149    /// ```
150    /// # use std::convert::*;
151    /// # use ion_c_sys::*;
152    /// # use ion_c_sys::reader::*;
153    /// # use ion_c_sys::result::*;
154    /// # use ion_c_sys::string::*;
155    /// # fn main() -> IonCResult<()> {
156    /// let mut reader = IonCReaderHandle::try_from("ab::cde::fghi::5")?;
157    /// assert_eq!(ION_TYPE_INT, reader.next()?);
158    /// let annotations = reader.get_annotations()?;
159    /// assert_eq!(
160    ///     vec!["ab", "cde", "fghi"].as_slice(),
161    ///     annotations.as_ref()
162    /// );
163    /// # Ok(())
164    /// # }
165    /// ```
166    fn get_annotations(&mut self) -> IonCResult<StrSlicesRef>;
167
168    /// Reads a `bool` value from the reader.
169    ///
170    /// ## Usage
171    /// ```
172    /// # use std::convert::*;
173    /// # use ion_c_sys::*;
174    /// # use ion_c_sys::reader::*;
175    /// # use ion_c_sys::result::*;
176    /// # fn main() -> IonCResult<()> {
177    /// let mut reader = IonCReaderHandle::try_from("true")?;
178    /// assert_eq!(ION_TYPE_BOOL, reader.next()?);
179    /// assert!(reader.read_bool()?);
180    /// # Ok(())
181    /// # }
182    /// ```
183    fn read_bool(&mut self) -> IonCResult<bool>;
184
185    /// Reads an `int` value from the reader.
186    ///
187    /// ## Usage
188    /// ```
189    /// # use std::convert::*;
190    /// # use ion_c_sys::*;
191    /// # use ion_c_sys::reader::*;
192    /// # use ion_c_sys::result::*;
193    /// # fn main() -> IonCResult<()> {
194    /// let mut reader = IonCReaderHandle::try_from("42")?;
195    /// assert_eq!(ION_TYPE_INT, reader.next()?);
196    /// assert_eq!(42, reader.read_i64()?);
197    /// # Ok(())
198    /// # }
199    /// ```
200    fn read_i64(&mut self) -> IonCResult<i64>;
201
202    /// Reads an `int` value from the reader as a `BigInt`.
203    ///
204    /// ## Usage
205    /// ```
206    /// # use std::convert::*;
207    /// # use ion_c_sys::*;
208    /// # use ion_c_sys::reader::*;
209    /// # use ion_c_sys::result::*;
210    /// # use num_bigint::BigInt;
211    /// # fn main() -> IonCResult<()> {
212    /// let mut reader = IonCReaderHandle::try_from("0x5195a4b154400e07dee3a7378c403b2d5dd6dd58735")?;
213    /// assert_eq!(ION_TYPE_INT, reader.next()?);
214    /// assert_eq!(
215    ///   BigInt::parse_bytes(b"1907775120294590714755986204580814176547217067050805", 10).unwrap(),
216    ///   reader.read_bigint()?
217    /// );
218    /// # Ok(())
219    /// # }
220    /// ```
221    fn read_bigint(&mut self) -> IonCResult<BigInt>;
222
223    /// Reads a `float` value from the reader.
224    ///
225    /// ## Usage
226    /// ```
227    /// # use std::convert::*;
228    /// # use ion_c_sys::*;
229    /// # use ion_c_sys::reader::*;
230    /// # use ion_c_sys::result::*;
231    /// # fn main() -> IonCResult<()> {
232    /// let mut reader = IonCReaderHandle::try_from("3.0e0")?;
233    /// assert_eq!(ION_TYPE_FLOAT, reader.next()?);
234    /// assert_eq!(3.0, reader.read_f64()?);
235    /// # Ok(())
236    /// # }
237    /// ```
238    fn read_f64(&mut self) -> IonCResult<f64>;
239
240    /// Reads a `bigdecimal` value from the reader.
241    ///
242    /// ## Usage
243    /// ```
244    /// # use std::convert::*;
245    /// # use bigdecimal::BigDecimal;
246    /// # use ion_c_sys::*;
247    /// # use ion_c_sys::decimal::*;
248    /// # use ion_c_sys::reader::*;
249    /// # use ion_c_sys::result::*;
250    /// # fn main() -> IonCResult<()> {
251    /// let mut reader = IonCReaderHandle::try_from("3.0")?;
252    /// assert_eq!(ION_TYPE_DECIMAL, reader.next()?);
253    /// let value = BigDecimal::parse_bytes(b"30E-1", 10).unwrap();
254    /// assert_eq!(value, reader.read_bigdecimal()?);
255    /// # Ok(())
256    /// # }
257    /// ```
258    fn read_bigdecimal(&mut self) -> IonCResult<BigDecimal>;
259
260    /// Reads a `timestamp` value from the reader.
261    ///
262    /// ## Usage
263    /// ```
264    /// # use std::convert::*;
265    /// # use ion_c_sys::*;
266    /// # use chrono::DateTime;
267    /// # use ion_c_sys::timestamp::*;
268    /// # use ion_c_sys::timestamp::Mantissa::*;
269    /// # use ion_c_sys::timestamp::TSPrecision::*;
270    /// # use ion_c_sys::timestamp::TSOffsetKind::*;
271    /// # use ion_c_sys::reader::*;
272    /// # use ion_c_sys::result::*;
273    /// # fn main() -> IonCResult<()> {
274    /// let mut reader = IonCReaderHandle::try_from("2020-10-10T12:34:45.123-00:00")?;
275    /// assert_eq!(ION_TYPE_TIMESTAMP, reader.next()?);
276    /// let ion_dt = reader.read_datetime()?;
277    ///
278    /// // the point in time should be the same
279    /// let expected_dt = DateTime::parse_from_rfc3339("2020-10-10T12:34:45.123Z").unwrap();
280    /// assert_eq!(&expected_dt, ion_dt.as_datetime());
281    ///
282    /// // precision should be millisecond level
283    /// if let Fractional(Digits(digits)) = ion_dt.precision() {
284    ///     assert_eq!(3, *digits);
285    /// } else {
286    ///     assert!(false, "Expected digits precision!");
287    /// }
288    ///
289    /// // we should have an unknown offset
290    /// assert_eq!(UnknownOffset, ion_dt.offset_kind());
291    /// # Ok(())
292    /// # }
293    /// ```
294    fn read_datetime(&mut self) -> IonCResult<IonDateTime>;
295
296    /// Reads a `string`/`symbol` value from the reader.
297    ///
298    /// ## Usage
299    /// ```
300    /// # use std::convert::*;
301    /// # use ion_c_sys::*;
302    /// # use ion_c_sys::reader::*;
303    /// # use ion_c_sys::result::*;
304    /// # fn main() -> IonCResult<()> {
305    /// let mut reader = IonCReaderHandle::try_from("\"🦄\" '✨'")?;
306    /// assert_eq!(ION_TYPE_STRING, reader.next()?);
307    /// assert_eq!("🦄", reader.read_string()?.as_str());
308    /// assert_eq!(ION_TYPE_SYMBOL, reader.next()?);
309    /// assert_eq!("✨", reader.read_string()?.as_str());
310    /// # Ok(())
311    /// # }
312    /// ```
313    fn read_string(&mut self) -> IonCResult<StrSliceRef>;
314
315    /// Reads a `clob`/`blob` value from the reader.
316    ///
317    /// This method implements a vector on the heap to store a copy of the LOB.
318    /// If this is not desired, use the low-level length and read methods directly.
319    ///
320    /// ## Usage
321    /// ```
322    /// # use std::convert::*;
323    /// # use ion_c_sys::*;
324    /// # use ion_c_sys::reader::*;
325    /// # use ion_c_sys::result::*;
326    /// # fn main() -> IonCResult<()> {
327    /// let mut reader = IonCReaderHandle::try_from("{{\"hello\"}} {{d29ybGQ=}} {{}}")?;
328    /// assert_eq!(ION_TYPE_CLOB, reader.next()?);
329    /// assert_eq!(b"hello", reader.read_bytes()?.as_slice());
330    /// assert_eq!(ION_TYPE_BLOB, reader.next()?);
331    /// assert_eq!(b"world", reader.read_bytes()?.as_slice());
332    /// assert_eq!(ION_TYPE_BLOB, reader.next()?);
333    /// assert_eq!(b"", reader.read_bytes()?.as_slice());
334    /// # Ok(())
335    /// # }
336    /// ```
337    fn read_bytes(&mut self) -> IonCResult<Vec<u8>>;
338
339    /// Returns the current position. TODO: blah blah.
340    fn pos(&self) -> IonCResult<Position>;
341}
342
343/// Wrapper over `hREADER` to make it easier to use readers in IonC correctly.
344///
345/// Specifically supports the `Drop` trait to make sure `ion_reader_close` is run.
346/// Access to the underlying `hREADER` pointer is done by de-referencing the handle.
347pub struct IonCReaderHandle<'a> {
348    reader: hREADER,
349    /// Placeholder to tie our lifecycle back to the source of the data--which might not
350    /// actually be a byte slice (if we constructed this from a file or Ion C stream callback)
351    referent: PhantomData<&'a [u8]>,
352}
353
354impl<'a> IonCReaderHandle<'a> {
355    /// Constructs a reader handle from a byte slice and given options.
356    pub fn try_from_buf(
357        src: &'a [u8],
358        options: &mut ION_READER_OPTIONS,
359    ) -> Result<Self, IonCError> {
360        let mut reader = ptr::null_mut();
361        ionc!(ion_reader_open_buffer(
362            &mut reader,
363            // Ion C promises not to mutate this buffer!
364            src.as_ptr() as *mut u8,
365            src.len().try_into()?,
366            options,
367        ))?;
368        Ok(IonCReaderHandle {
369            reader,
370            referent: PhantomData::default(),
371        })
372    }
373}
374impl<'a> IonCReader for IonCReaderHandle<'a> {
375    #[position_error]
376    #[inline]
377    fn next(&mut self) -> IonCResult<ION_TYPE> {
378        let mut tid = ptr::null_mut();
379        ionc!(ion_reader_next(self.reader, &mut tid))?;
380
381        Ok(tid)
382    }
383
384    #[position_error]
385    #[inline]
386    fn get_type(&self) -> IonCResult<ION_TYPE> {
387        let mut tid = ptr::null_mut();
388        ionc!(ion_reader_get_type(self.reader, &mut tid))?;
389
390        Ok(tid)
391    }
392
393    #[position_error]
394    #[inline]
395    fn step_in(&mut self) -> IonCResult<()> {
396        ionc!(ion_reader_step_in(self.reader))
397    }
398
399    #[position_error]
400    #[inline]
401    fn step_out(&mut self) -> IonCResult<()> {
402        ionc!(ion_reader_step_out(self.reader))
403    }
404
405    #[position_error]
406    #[inline]
407    fn depth(&self) -> IonCResult<i32> {
408        let mut depth = 0;
409        ionc!(ion_reader_get_depth(self.reader, &mut depth))?;
410
411        Ok(depth)
412    }
413
414    #[position_error]
415    #[inline]
416    fn is_null(&self) -> IonCResult<bool> {
417        let mut is_null = 0;
418        ionc!(ion_reader_is_null(self.reader, &mut is_null))?;
419
420        Ok(is_null != 0)
421    }
422
423    #[position_error]
424    #[inline]
425    fn is_in_struct(&self) -> IonCResult<bool> {
426        let mut is_in_struct = 0;
427        ionc!(ion_reader_is_in_struct(self.reader, &mut is_in_struct))?;
428
429        Ok(is_in_struct != 0)
430    }
431
432    #[position_error]
433    #[inline]
434    fn get_field_name(&mut self) -> IonCResult<StrSliceRef> {
435        let mut field = ION_STRING::default();
436        ionc!(ion_reader_get_field_name(self.reader, &mut field))?;
437
438        // make a str slice that is tied to our lifetime
439        let field_str = field.as_str(PhantomData::<&'a u8>::default())?;
440        Ok(StrSliceRef::new(self, field_str))
441    }
442
443    fn get_annotations(&mut self) -> IonCResult<StrSlicesRef> {
444        // determine how many annotations are available
445        let mut raw_len = 0;
446        ionc!(ion_reader_get_annotation_count(self.reader, &mut raw_len))?;
447
448        let len: usize = raw_len.try_into()?;
449        let mut annotations = Vec::new();
450        let mut curr = ION_STRING::default();
451        for i in 0..len {
452            ionc!(ion_reader_get_an_annotation(
453                self.reader,
454                i as c_int,
455                &mut curr
456            ))?;
457            // make a str slice that is tied to our lifetime
458            annotations.push(curr.as_str(PhantomData::<&'a u8>::default())?);
459        }
460
461        Ok(StrSlicesRef::new(self, annotations))
462    }
463
464    #[position_error]
465    #[inline]
466    fn read_bool(&mut self) -> IonCResult<bool> {
467        let mut value = 0;
468        ionc!(ion_reader_read_bool(self.reader, &mut value))?;
469
470        Ok(value != 0)
471    }
472
473    #[position_error]
474    #[inline]
475    fn read_i64(&mut self) -> IonCResult<i64> {
476        let mut value = 0;
477        ionc!(ion_reader_read_int64(self.reader, &mut value))?;
478
479        Ok(value)
480    }
481
482    #[position_error]
483    #[inline]
484    fn read_bigint(&mut self) -> IonCResult<BigInt> {
485        let mut value = ION_INT::default();
486        ionc!(ion_reader_read_ion_int(self.reader, &mut value))?;
487
488        value.try_to_bigint()
489    }
490
491    #[position_error]
492    #[inline]
493    fn read_f64(&mut self) -> IonCResult<f64> {
494        let mut value = 0.0;
495        ionc!(ion_reader_read_double(self.reader, &mut value))?;
496
497        Ok(value)
498    }
499
500    #[position_error]
501    #[inline]
502    fn read_bigdecimal(&mut self) -> IonCResult<BigDecimal> {
503        let mut value = ION_DECIMAL::default();
504        ionc!(ion_reader_read_ion_decimal(self.reader, &mut value))?;
505
506        value.try_to_bigdecimal()
507    }
508
509    #[position_error]
510    #[inline]
511    fn read_datetime(&mut self) -> IonCResult<IonDateTime> {
512        let mut value = ION_TIMESTAMP::default();
513        ionc!(ion_reader_read_timestamp(self.reader, &mut value))?;
514
515        value.try_to_iondt()
516    }
517
518    #[position_error]
519    #[inline]
520    fn read_string(&mut self) -> IonCResult<StrSliceRef> {
521        let mut value = ION_STRING::default();
522        ionc!(ion_reader_read_string(self.reader, &mut value))?;
523
524        // make a str slice that is tied to our lifetime
525        let str_ref = value.as_str(PhantomData::<&'a u8>::default())?;
526        Ok(StrSliceRef::new(self, str_ref))
527    }
528
529    #[position_error]
530    #[inline]
531    fn read_bytes(&mut self) -> IonCResult<Vec<u8>> {
532        let mut len = 0;
533        ionc!(ion_reader_get_lob_size(self.reader, &mut len))?;
534        if len == 0 {
535            // XXX short-circuit the empty case
536            // until amzn/ion-c#233 is fixed this will panic, but there
537            // is no reason to call into Ion C in this case anyhow
538            return Ok(Vec::new());
539        }
540
541        let mut read_len = 0;
542        let mut buf = vec![0; len.try_into()?];
543        ionc!(ion_reader_read_lob_bytes(
544            self.reader,
545            buf.as_mut_ptr(),
546            buf.len().try_into()?,
547            &mut read_len
548        ))?;
549        if len != read_len {
550            Err(IonCError::from(ion_error_code_IERR_INVALID_STATE))
551        } else {
552            Ok(buf)
553        }
554    }
555
556    // *NOT* annotated with `#[position_error]` - if reading the current
557    // position is an error, then we don't want to include the position in the
558    // error!
559    //
560    // This method is actually implemented in `reader_current_pos`. Otherwise,
561    // various reader trait methods require additional borrows.
562    #[inline]
563    fn pos(&self) -> IonCResult<Position> {
564        reader_current_pos(&self.reader)
565    }
566}
567
568/// Asks `reader` for its current position and includes it in `err`.
569///
570/// If the position is already known, then this method does nothing. If reading
571/// the position returns an error, then this method also does nothing!
572///
573/// This method is defined on the reader handle directly because of partial
574/// borrow requirements in the implementation of the `Reader` trait.
575pub fn include_current_position(reader: &hREADER, err: IonCError) -> IonCError {
576    if !std::matches!(err.position, Position::Unknown) {
577        return err;
578    }
579
580    let pos = match reader_current_pos(reader) {
581        Ok(pos) => pos,
582        Err(_) => return err, // the original, not the one from failing to read the pos
583    };
584
585    err.with_position(pos)
586}
587
588#[inline]
589fn reader_current_pos(reader: &hREADER) -> IonCResult<Position> {
590    let mut bytes: i64 = -1;
591    let mut line = -1;
592    let mut offset = -1;
593    ionc!(ion_reader_get_position(
594        *reader,
595        &mut bytes,
596        &mut line,
597        &mut offset
598    ))?;
599
600    Ok(match (bytes, line, offset) {
601        (b, -1, -1) if b > 0 => Position::Offset(b),
602        (b, l, o) if b > 0 && l > 0 && o > 0 => Position::OffsetLineColumn(b, LineColumn(l, o)),
603        // Should never happen!
604        _ => Position::Unknown,
605    })
606}
607
608impl<'a> TryFrom<&'a [u8]> for IonCReaderHandle<'a> {
609    type Error = IonCError;
610
611    /// Constructs a reader from a byte slice with the default options.
612    #[inline]
613    fn try_from(src: &'a [u8]) -> Result<Self, Self::Error> {
614        Self::try_from_buf(src, &mut ION_READER_OPTIONS::default())
615    }
616}
617
618impl<'a> TryFrom<&'a str> for IonCReaderHandle<'a> {
619    type Error = IonCError;
620    /// Constructs a reader from a str slice with the default options.
621    #[inline]
622    fn try_from(src: &'a str) -> Result<Self, Self::Error> {
623        Self::try_from(src.as_bytes())
624    }
625}
626
627impl Deref for IonCReaderHandle<'_> {
628    type Target = hREADER;
629
630    #[inline]
631    fn deref(&self) -> &Self::Target {
632        &self.reader
633    }
634}
635
636impl DerefMut for IonCReaderHandle<'_> {
637    #[inline]
638    fn deref_mut(&mut self) -> &mut Self::Target {
639        &mut self.reader
640    }
641}
642
643impl Drop for IonCReaderHandle<'_> {
644    fn drop(&mut self) {
645        if !self.reader.is_null() {
646            ionc!(ion_reader_close(self.reader)).unwrap()
647        }
648    }
649}
650
651#[cfg(test)]
652mod reader_tests {
653    use super::*;
654
655    #[test]
656    fn position_error() -> IonCResult<()> {
657        let data = r#"{foo:"bar",baz:"#;
658        let mut reader = IonCReaderHandle::try_from(data)?;
659        reader.next()?;
660
661        // `baz` has a field but not value - boom!
662        let err = match reader.next() {
663            Err(e) => e,
664            Ok(t) => panic!("expected an error, found a {:?}", t),
665        };
666
667        assert_eq!(err.position, Position::text(15, 1, 16));
668
669        Ok(())
670    }
671}