1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
//! Type definitions for wrappers which parse interleaved data.

use crate::{BinRead, BinResult, VecArgs};
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use core::fmt;

/// A parser for data which consists of values of type `T` interleaved with
/// other values of type `P`.
///
/// To use this parser, you must specify the parsing strategy by selecting
/// either [`separated()`] or [`separated_trailing()`] using [`parse_with`].
///
/// [`separated()`]: Self::separated
/// [`separated_trailing()`]: Self::separated_trailing
/// [`parse_with`]: crate::docs::attribute#custom-parserswriters
///
/// Consider using a `Vec<(T, P)>` or `(Vec<(T, P)>, Option<T>>)` instead if you
/// do not need the parsed data to be transformed into a structure of arrays.
///
/// # Examples
///
/// ```
/// # use binrw::{prelude::*, io::Cursor};
/// use binrw::punctuated::Punctuated;
///
/// #[derive(BinRead)]
/// struct MyList {
///     #[br(parse_with = Punctuated::separated)]
///     #[br(count = 3)]
///     x: Punctuated<u16, u8>,
/// }
///
/// # let mut x = Cursor::new(b"\0\x03\0\0\x02\x01\0\x01");
/// # let y: MyList = x.read_be().unwrap();
/// # assert_eq!(*y.x, vec![3, 2, 1]);
/// # assert_eq!(y.x.separators, vec![0, 1]);
/// ```
pub struct Punctuated<T: BinRead, P: BinRead> {
    /// The data values.
    data: Vec<T>,

    /// The separator values.
    pub separators: Vec<P>,
}

impl<T, P> Punctuated<T, P>
where
    T: BinRead,
    P: for<'a> BinRead<Args<'a> = ()>,
{
    /// Parses values of type `T` separated by values of type `P` without a
    /// trailing separator value.
    ///
    /// Requires a count to be passed via `#[br(count)]`.
    ///
    /// # Errors
    ///
    /// If reading fails, an [`Error`](crate::Error) variant will be returned.
    ///
    /// # Example
    ///
    /// ```
    /// # use binrw::{prelude::*, io::Cursor};
    /// use binrw::punctuated::Punctuated;
    ///
    /// #[derive(BinRead)]
    /// struct MyList {
    ///     #[br(parse_with = Punctuated::separated)]
    ///     #[br(count = 3)]
    ///     x: Punctuated<u16, u8>,
    /// }
    ///
    /// # let mut x = Cursor::new(b"\0\x03\0\0\x02\x01\0\x01");
    /// # let y: MyList = x.read_be().unwrap();
    /// # assert_eq!(*y.x, vec![3, 2, 1]);
    /// # assert_eq!(y.x.separators, vec![0, 1]);
    /// ```
    #[crate::parser(reader, endian)]
    pub fn separated<'a>(args: VecArgs<T::Args<'a>>, ...) -> BinResult<Self>
    where
        T::Args<'a>: Clone,
    {
        let mut data = Vec::with_capacity(args.count);
        let mut separators = Vec::with_capacity(args.count.max(1) - 1);

        for i in 0..args.count {
            data.push(T::read_options(reader, endian, args.inner.clone())?);
            if i + 1 != args.count {
                separators.push(P::read_options(reader, endian, ())?);
            }
        }

        Ok(Self { data, separators })
    }

    /// Parses values of type `T` interleaved with values of type `P`, including
    /// a trailing `P`.
    ///
    /// Requires a count to be passed via `#[br(count)]`.
    ///
    /// # Errors
    ///
    /// If reading fails, an [`Error`](crate::Error) variant will be returned.
    #[crate::parser(reader, endian)]
    pub fn separated_trailing<'a>(args: VecArgs<T::Args<'a>>, ...) -> BinResult<Self>
    where
        T::Args<'a>: Clone,
    {
        let mut data = Vec::with_capacity(args.count);
        let mut separators = Vec::with_capacity(args.count);

        for _ in 0..args.count {
            data.push(T::read_options(reader, endian, args.inner.clone())?);
            separators.push(P::read_options(reader, endian, ())?);
        }

        Ok(Self { data, separators })
    }

    /// Consumes this object, returning the data values while dropping the
    /// separator values.
    ///
    /// If you never use the separator values, consider using the [`pad_after`]
    /// directive to skip over data while parsing instead of reading it into
    /// memory and then discarding it.
    ///
    /// [`pad_after`]: crate::docs::attribute#padding-and-alignment
    #[must_use]
    pub fn into_values(self) -> Vec<T> {
        self.data
    }
}

impl<T: BinRead + fmt::Debug, P: BinRead> fmt::Debug for Punctuated<T, P> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.data.fmt(f)
    }
}

impl<T: BinRead, P: BinRead> core::ops::Deref for Punctuated<T, P> {
    type Target = Vec<T>;

    fn deref(&self) -> &Self::Target {
        &self.data
    }
}

impl<T: BinRead, P: BinRead> core::ops::DerefMut for Punctuated<T, P> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.data
    }
}