Skip to main content

aws_multipart_upload/encoder/
csv_encoder.rs

1use csv::{Error as CsvError, Writer, WriterBuilder};
2use serde::Serialize;
3
4use super::{EncodeError, EncodeErrorKind, PartEncoder};
5use crate::request::{PartBody, PartNumber};
6
7/// `CsvEncoder` implements `Encoder` with a CSV writer.
8#[derive(Debug, Clone, Copy)]
9pub struct CsvEncoder {
10    has_header: bool,
11    double_quotes: bool,
12}
13
14impl CsvEncoder {
15    /// Create a new `CsvEncoder` from a configured `CsvEncoderBuilder`.
16    pub fn new() -> Self {
17        Self::default()
18    }
19
20    /// Whether to skip writing a header row in the first part.
21    ///
22    /// By default a header row is written as the first row in the first part.
23    pub fn skip_header(self) -> Self {
24        Self { has_header: false, double_quotes: self.double_quotes }
25    }
26
27    /// When there are quotes in field data, escape them instead of writing
28    /// double quotes.
29    ///
30    /// By default, quotes in fields are converted to `""`.  With this enabled,
31    /// this instead becomes `\"`.
32    pub fn escape_quotes(self) -> Self {
33        Self { double_quotes: false, has_header: self.has_header }
34    }
35
36    fn csv_writer(
37        self,
38        wr: &mut PartBody,
39        is_first: bool,
40    ) -> Writer<&mut PartBody> {
41        let mut builder = WriterBuilder::new();
42        if is_first && wr.is_empty() {
43            builder.has_headers(self.has_header)
44        } else {
45            // Otherwise every row would be written with a header above it.
46            builder.has_headers(false)
47        }
48        .double_quote(self.double_quotes)
49        .from_writer(wr)
50    }
51}
52
53impl Default for CsvEncoder {
54    fn default() -> Self {
55        Self { has_header: true, double_quotes: true }
56    }
57}
58
59impl<Item: Serialize> PartEncoder<Item> for CsvEncoder {
60    type Error = CsvError;
61
62    fn encode(
63        &mut self,
64        part: &mut PartBody,
65        part_number: PartNumber,
66        item: Item,
67    ) -> Result<(), Self::Error> {
68        let mut writer = self.csv_writer(part, part_number.is_first());
69        writer.serialize(item)?;
70        writer.flush()?;
71        Ok(())
72    }
73}
74
75impl EncodeError for CsvError {
76    fn message(&self) -> String {
77        self.to_string()
78    }
79
80    fn kind(&self) -> EncodeErrorKind {
81        match self.kind() {
82            csv::ErrorKind::Io(_) => EncodeErrorKind::Io,
83            csv::ErrorKind::UnequalLengths { .. }
84            | csv::ErrorKind::Utf8 { .. }
85            | csv::ErrorKind::Deserialize { .. }
86            | csv::ErrorKind::Serialize(_) => EncodeErrorKind::Data,
87            csv::ErrorKind::Seek => EncodeErrorKind::Eof,
88            _ => EncodeErrorKind::Unknown,
89        }
90    }
91}