aws_multipart_upload/codec/
csv.rs

1use bytes::{BufMut, BytesMut};
2use csv::{Terminator, Writer as CsvWriter, WriterBuilder};
3use serde::Serialize;
4use std::io::Write;
5use tokio_util::codec::Encoder;
6
7use crate::AwsError;
8
9#[derive(Debug, thiserror::Error)]
10pub enum CsvCodecError {
11    #[error("csv error in encoding bytes {0}")]
12    CsvWriter(#[from] csv::Error),
13    #[error("io error {0}")]
14    Io(#[from] std::io::Error),
15}
16
17impl From<CsvCodecError> for AwsError {
18    fn from(value: CsvCodecError) -> Self {
19        Self::Codec(value.to_string())
20    }
21}
22
23/// A CSV encoder.
24#[derive(Debug, Clone, Default)]
25pub struct CsvCodec {
26    pub has_headers: bool,
27    pub term: Terminator,
28}
29
30impl CsvCodec {
31    pub fn new() -> Self {
32        Self::default()
33    }
34
35    pub fn with_headers(mut self) -> Self {
36        self.has_headers = true;
37        self
38    }
39
40    pub fn set_terminator(mut self, term: Terminator) -> Self {
41        self.term = term;
42        self
43    }
44
45    pub fn from_writer<W: Write>(&self, wtr: W) -> CsvWriter<W> {
46        WriterBuilder::new()
47            .has_headers(self.has_headers)
48            .terminator(self.term)
49            .from_writer(wtr)
50    }
51}
52
53impl<Item> Encoder<Item> for CsvCodec
54where
55    Item: Serialize + std::fmt::Debug,
56{
57    type Error = CsvCodecError;
58
59    fn encode(&mut self, item: Item, dst: &mut BytesMut) -> Result<(), Self::Error> {
60        let mut csv = self.from_writer(vec![]);
61        // This writes a CSV row with newline or CLRF character as the line
62        // terminator, so there is no need to reserve "+1" and write out the
63        // line terminating character ourselves.
64        csv.serialize(item)?;
65        let inner = csv.into_inner().map_err(|e| e.into_error())?;
66        dst.reserve(inner.len());
67        dst.put_slice(&inner);
68        Ok(())
69    }
70}