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/// [`set_max_record_len`](DeserializerConfig::set_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,
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,
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 context(&self) -> deser_core::Context {
121 self.config.context().clone()
122 }
123
124 fn frame(&mut self, input: &[u8], eof: bool) -> Result<Frame, Error> {
125 self.state.frame(&self.config, input, eof)
126 }
127
128 fn drive_frame<'de>(
129 &mut self,
130 frame: &'de [u8],
131 driver: &mut DeserializeDriver<'_, 'de>,
132 ) -> Result<(), Error> {
133 self.state
134 .emit_record(&self.config, frame, 0, false, driver)
135 .map_err(|mut err| {
136 err.resolve_position(frame);
137 err
138 })
139 }
140
141 fn is_text(&self) -> bool {
142 true
143 }
144}
145
146#[cfg(feature = "io")]
147impl DeserializerConfig {
148 /// Creates a reader of a stream of records (see
149 /// [`deser::io::Reader`](deser_core::io::Reader)).
150 ///
151 /// Every value is a record, see [`StreamDeserializer`].
152 pub fn reader<R: Read>(&self, reader: R) -> deser_core::io::Reader<R, StreamDeserializer> {
153 deser_core::io::Reader::new(reader, StreamDeserializer::with_config(self.clone()))
154 }
155
156 /// Deserializes the records of a reader.
157 ///
158 /// See [`from_reader`].
159 pub fn from_reader<T: DeserializeOwned, R: Read>(&self, mut reader: R) -> Result<T, Error> {
160 let mut input = alloc::vec::Vec::new();
161 reader.read_to_end(&mut input)?;
162 self.from_slice(&input)
163 }
164}
165
166/// Deserializes the records of a reader.
167///
168/// This works like [`from_str`](crate::from_str): all records are
169/// deserialized as a sequence. The reader is read to the end, it does not
170/// need to be buffered. To read one record at a time use
171/// [`DeserializerConfig::reader`].
172///
173/// ```
174/// use std::collections::BTreeMap;
175///
176/// let rows: Vec<BTreeMap<String, u32>> =
177/// deser_csv::from_reader(&b"a,b\n1,2\n"[..]).unwrap();
178/// assert_eq!(rows[0]["b"], 2);
179/// ```
180#[cfg(feature = "io")]
181pub fn from_reader<T: DeserializeOwned, R: Read>(reader: R) -> Result<T, Error> {
182 DeserializerConfig::new().from_reader(reader)
183}