deser_csv/stream.rs
1//! Reading delimited text from streams.
2#[cfg(feature = "io")]
3use std::io::Read;
4
5use alloc::string::String;
6use deser_core::Error;
7#[cfg(feature = "io")]
8use deser_core::de::DeserializeOwned;
9use deser_core::de::{self, DeserializeDriver, Frame};
10
11use crate::de::{DeserializerConfig, StreamState};
12
13/// Reads the records of a stream (see [`deser::stream`](deser_core::stream)).
14///
15/// Every value of the stream is a record. The names of the columns are
16/// read before the first record (see [`headers`](Self::headers)). Errors
17/// of records (like fields that do not fit the type or records with the
18/// wrong number of fields) only discard the record, reading continues with
19/// the next one. Errors of the stream (like a record that exceeds
20/// [`max_record_len`](DeserializerConfig::max_record_len)) end it.
21///
22/// ```
23/// # #[cfg(feature = "io")] {
24/// use deser_csv::DeserializerConfig;
25///
26/// #[derive(deser::Deserialize)]
27/// struct Row {
28/// name: String,
29/// age: u32,
30/// }
31///
32/// let mut reader =
33/// DeserializerConfig::new().reader(&b"name,age\njane,42\njohn,23\n"[..]);
34/// let rows = reader.iter::<Row>().collect::<Result<Vec<_>, _>>().unwrap();
35/// assert_eq!(rows[1].age, 23);
36/// # }
37/// ```
38///
39/// Records are parsed like with a [`Deserializer`](crate::Deserializer), so
40/// they can borrow from the stream's buffer (see
41/// [`InputBuffer::deserialize`](deser_core::stream::InputBuffer::deserialize)).
42/// The input ranges (and thus locations) of fields refer to the start of
43/// their record.
44#[derive(Debug)]
45pub struct StreamDeserializer {
46 config: DeserializerConfig,
47 state: StreamState,
48}
49
50impl Default for StreamDeserializer {
51 fn default() -> StreamDeserializer {
52 StreamDeserializer::new()
53 }
54}
55
56impl StreamDeserializer {
57 /// Creates a stream deserializer.
58 pub fn new() -> StreamDeserializer {
59 StreamDeserializer::with_config(&DeserializerConfig::new())
60 }
61
62 /// Creates a stream deserializer with the given configuration.
63 pub fn with_config(config: &DeserializerConfig) -> StreamDeserializer {
64 StreamDeserializer {
65 config: config.clone(),
66 state: StreamState::default(),
67 }
68 }
69
70 /// Creates a stream deserializer for a stream which continues with the
71 /// given names of the columns.
72 ///
73 /// The stream does not start with names (they are not read from the
74 /// first record), for instance because it's the second half of a file:
75 ///
76 /// ```
77 /// # #[cfg(feature = "io")] {
78 /// use deser::io::Reader;
79 /// use deser_csv::{DeserializerConfig, StreamDeserializer};
80 ///
81 /// #[derive(deser::Deserialize)]
82 /// struct Row {
83 /// name: String,
84 /// age: u32,
85 /// }
86 ///
87 /// let de = StreamDeserializer::with_headers(&DeserializerConfig::new(), ["name", "age"]);
88 /// let mut reader = Reader::new(&b"jane,42\n"[..], de);
89 /// let row: Row = reader.read().unwrap().unwrap();
90 /// assert_eq!((row.name.as_str(), row.age), ("jane", 42));
91 /// # }
92 /// ```
93 pub fn with_headers<I, S>(config: &DeserializerConfig, names: I) -> StreamDeserializer
94 where
95 I: IntoIterator<Item = S>,
96 S: Into<String>,
97 {
98 StreamDeserializer {
99 config: config.clone(),
100 state: StreamState::with_headers(names.into_iter().map(Into::into).collect()),
101 }
102 }
103
104 /// Returns the configuration.
105 pub fn config(&self) -> &DeserializerConfig {
106 &self.config
107 }
108
109 /// Returns the names of the columns.
110 ///
111 /// This is `None` until the first record was read (with
112 /// [`Headers::First`](crate::Headers::First)) or if records have no
113 /// names ([`Headers::None`](crate::Headers::None)).
114 pub fn headers(&self) -> Option<&[String]> {
115 self.state.headers()
116 }
117}
118
119impl de::StreamDeserializer for StreamDeserializer {
120 fn frame(&mut self, input: &[u8], eof: bool) -> Result<Frame, Error> {
121 self.state.frame(&self.config, input, eof)
122 }
123
124 fn drive_frame<'de>(
125 &mut self,
126 frame: &'de [u8],
127 driver: &mut DeserializeDriver<'_, 'de>,
128 ) -> Result<(), Error> {
129 self.state
130 .emit_record(&self.config, frame, 0, driver)
131 .map_err(|err| err.resolve_position(frame))
132 }
133
134 fn is_text(&self) -> bool {
135 true
136 }
137}
138
139#[cfg(feature = "io")]
140impl DeserializerConfig {
141 /// Creates a reader of a stream of records (see
142 /// [`deser::io::Reader`](deser_core::io::Reader)).
143 ///
144 /// Every value is a record, see [`StreamDeserializer`].
145 pub fn reader<R: Read>(&self, reader: R) -> deser_core::io::Reader<R, StreamDeserializer> {
146 deser_core::io::Reader::new(reader, StreamDeserializer::with_config(self))
147 }
148
149 /// Deserializes the records of a reader.
150 ///
151 /// See [`from_reader`](crate::from_reader).
152 pub fn from_reader<T: DeserializeOwned, R: Read>(&self, mut reader: R) -> Result<T, Error> {
153 let mut input = alloc::vec::Vec::new();
154 reader.read_to_end(&mut input)?;
155 self.from_slice(&input)
156 }
157}
158
159/// Deserializes the records of a reader.
160///
161/// This works like [`from_str`](crate::from_str): all records are
162/// deserialized as a sequence. The reader is read to the end, it does not
163/// need to be buffered. To read one record at a time use
164/// [`DeserializerConfig::reader`].
165///
166/// ```
167/// use std::collections::BTreeMap;
168///
169/// let rows: Vec<BTreeMap<String, u32>> =
170/// deser_csv::from_reader(&b"a,b\n1,2\n"[..]).unwrap();
171/// assert_eq!(rows[0]["b"], 2);
172/// ```
173#[cfg(feature = "io")]
174pub fn from_reader<T: DeserializeOwned, R: Read>(reader: R) -> Result<T, Error> {
175 DeserializerConfig::new().from_reader(reader)
176}