use csv::{Error as CsvError, Writer, WriterBuilder};
use serde::Serialize;
use super::{EncodeError, EncodeErrorKind, PartEncoder};
use crate::request::{PartBody, PartNumber};
#[derive(Debug, Clone, Copy)]
pub struct CsvEncoder {
has_header: bool,
double_quotes: bool,
}
impl CsvEncoder {
pub fn new() -> Self {
Self::default()
}
pub fn skip_header(self) -> Self {
Self { has_header: false, double_quotes: self.double_quotes }
}
pub fn escape_quotes(self) -> Self {
Self { double_quotes: false, has_header: self.has_header }
}
fn csv_writer(
self,
wr: &mut PartBody,
is_first: bool,
) -> Writer<&mut PartBody> {
let mut builder = WriterBuilder::new();
if is_first && wr.is_empty() {
builder.has_headers(self.has_header)
} else {
builder.has_headers(false)
}
.double_quote(self.double_quotes)
.from_writer(wr)
}
}
impl Default for CsvEncoder {
fn default() -> Self {
Self { has_header: true, double_quotes: true }
}
}
impl<Item: Serialize> PartEncoder<Item> for CsvEncoder {
type Error = CsvError;
fn encode(
&mut self,
part: &mut PartBody,
part_number: PartNumber,
item: Item,
) -> Result<(), Self::Error> {
let mut writer = self.csv_writer(part, part_number.is_first());
writer.serialize(item)?;
writer.flush()?;
Ok(())
}
}
impl EncodeError for CsvError {
fn message(&self) -> String {
self.to_string()
}
fn kind(&self) -> EncodeErrorKind {
match self.kind() {
csv::ErrorKind::Io(_) => EncodeErrorKind::Io,
csv::ErrorKind::UnequalLengths { .. }
| csv::ErrorKind::Utf8 { .. }
| csv::ErrorKind::Deserialize { .. }
| csv::ErrorKind::Serialize(_) => EncodeErrorKind::Data,
csv::ErrorKind::Seek => EncodeErrorKind::Eof,
_ => EncodeErrorKind::Unknown,
}
}
}