1use alloc::{
2 string::{String, ToString},
3 vec::Vec,
4};
5
6use crate::error::WriteError;
7
8pub trait Write {
13 fn write(&mut self, buf: &[u8]) -> Result<(), WriteError>;
17 fn flush(&mut self) -> Result<(), WriteError>;
19}
20
21#[cfg(not(feature = "std"))]
22impl Write for Vec<u8> {
23 fn write(&mut self, buf: &[u8]) -> Result<(), WriteError> {
24 self.extend_from_slice(buf);
25 Ok(())
26 }
27 fn flush(&mut self) -> Result<(), WriteError> {
28 Ok(())
29 }
30}
31
32#[cfg(feature = "std")]
33impl<W: std::io::Write> Write for W {
34 fn write(&mut self, buf: &[u8]) -> Result<(), WriteError> {
35 self.write_all(buf)?;
36 Ok(())
37 }
38 fn flush(&mut self) -> Result<(), WriteError> {
39 self.flush()?;
40 Ok(())
41 }
42}
43
44pub struct Writer<W: Write> {
66 pub(crate) writer: Option<W>,
67 pub(crate) delimiter: u8,
68 pub(crate) flexible: bool,
69 pub(crate) num_fields: Option<usize>,
70 pub(crate) row_count: usize,
71 pub(crate) buf: Vec<u8>,
72 pub(crate) headers: Option<Vec<String>>,
73 pub(crate) headers_written: bool,
74 #[cfg(feature = "serde")]
75 pub(crate) ser_values: Vec<Option<Vec<u8>>>,
76 #[cfg(feature = "serde")]
77 pub(crate) ser_capture_buf: Vec<u8>,
78}
79
80impl<W: Write> Writer<W> {
81 pub fn new(writer: W) -> Self {
86 Writer {
87 writer: Some(writer),
88 delimiter: b',',
89 flexible: false,
90 num_fields: None,
91 row_count: 0,
92 buf: Vec::with_capacity(8192),
93 headers: None,
94 headers_written: false,
95 #[cfg(feature = "serde")]
96 ser_values: Vec::new(),
97 #[cfg(feature = "serde")]
98 ser_capture_buf: Vec::new(),
99 }
100 }
101
102 pub fn set_delimiter(mut self, byte: u8) -> Self {
111 self.delimiter = byte;
112 self
113 }
114
115 pub fn set_flexible(mut self, yes: bool) -> Self {
120 self.flexible = yes;
121 self
122 }
123
124 pub fn set_headers(mut self, headers: Vec<String>) -> Self {
130 self.headers = Some(headers);
131 self
132 }
133
134 pub fn headers(&self) -> Option<&[String]> {
136 self.headers.as_deref()
137 }
138
139 pub fn write_headers<I, T>(&mut self, headers: I) -> Result<(), WriteError>
156 where
157 I: IntoIterator<Item = T>,
158 T: AsRef<str>,
159 {
160 if self.headers_written {
161 return Err(WriteError::HeadersAlreadyWritten);
162 }
163
164 let strings: Vec<String> = headers.into_iter().map(|s| s.as_ref().to_string()).collect();
165 let count = strings.len();
166
167 for (i, s) in strings.iter().enumerate() {
168 if i > 0 {
169 self.buf.push(self.delimiter);
170 }
171 self.write_field(s.as_bytes())?;
172 }
173
174 self.buf.extend_from_slice(b"\r\n");
175 self.row_count += 1;
176 self.headers = Some(strings);
177 self.headers_written = true;
178 self.num_fields = Some(count);
179
180 if self.buf.len() >= 8192 {
181 self.flush()?;
182 }
183
184 Ok(())
185 }
186
187 pub fn write_row<I, T>(&mut self, row: I) -> Result<(), WriteError>
199 where
200 I: IntoIterator<Item = T>,
201 T: AsRef<[u8]>,
202 {
203 if self.flexible {
204 let mut first = true;
205 let mut field_count = 0;
206 for field in row {
207 if !first {
208 self.buf.push(self.delimiter);
209 }
210 first = false;
211 self.write_field(field.as_ref())?;
212 field_count += 1;
213 }
214 if self.num_fields.is_none() && field_count > 0 {
215 self.num_fields = Some(field_count);
216 }
217 self.buf.extend_from_slice(b"\r\n");
218 self.row_count += 1;
219 } else {
220 let buf_start = self.buf.len();
221 let mut field_count = 0;
222 for (i, field) in row.into_iter().enumerate() {
223 if i > 0 {
224 self.buf.push(self.delimiter);
225 }
226 self.write_field(field.as_ref())?;
227 field_count += 1;
228 }
229
230 match self.num_fields {
231 Some(expected) if field_count != expected => {
232 self.buf.truncate(buf_start);
233 return Err(WriteError::InconsistentFieldCount {
234 expected,
235 found: field_count,
236 row: self.row_count + 1,
237 });
238 }
239 None => {
240 self.num_fields = Some(field_count);
241 }
242 _ => {}
243 }
244
245 self.buf.extend_from_slice(b"\r\n");
246 self.row_count += 1;
247 }
248
249 if self.buf.len() >= 8192 {
250 self.flush()?;
251 }
252
253 Ok(())
254 }
255
256 fn write_field(&mut self, field: &[u8]) -> Result<(), WriteError> {
257 write_csv_field(&mut self.buf, self.delimiter, field);
258 Ok(())
259 }
260
261 pub fn flush(&mut self) -> Result<(), WriteError> {
263 if let Some(writer) = self.writer.as_mut() {
264 Write::write(writer, &self.buf)?;
265 Write::flush(writer)?;
266 }
267 self.buf.clear();
268 Ok(())
269 }
270
271 pub fn into_inner(mut self) -> Result<W, WriteError> {
274 self.flush()?;
275 Ok(self.writer.take().unwrap())
276 }
277}
278
279impl<W: Write> Drop for Writer<W> {
280 fn drop(&mut self) {
281 if self.writer.is_some() {
282 let _ = self.flush();
283 }
284 }
285}
286
287pub(crate) fn write_csv_field(buf: &mut Vec<u8>, delimiter: u8, field: &[u8]) {
291 let needs_quoting = field.is_empty()
292 || memchr::memchr3(delimiter, b'\r', b'\n', field).is_some()
293 || memchr::memchr(b'"', field).is_some();
294
295 if !needs_quoting {
296 buf.extend_from_slice(field);
297 return;
298 }
299
300 buf.push(b'"');
301
302 let mut pos = 0;
303 while let Some(quote_offset) = memchr::memchr(b'"', &field[pos..]) {
304 let abs_pos = pos + quote_offset;
305 buf.extend_from_slice(&field[pos..abs_pos]);
306 buf.extend_from_slice(b"\"\"");
307 pos = abs_pos + 1;
308 }
309 buf.extend_from_slice(&field[pos..]);
310 buf.push(b'"');
311}