spring_batch_rs/item/csv/csv_writer.rs
1use std::{cell::RefCell, fs::File, io::Write, path::Path};
2
3use csv::{Writer, WriterBuilder};
4use serde::Serialize;
5
6use crate::{
7 core::item::{ItemWriter, ItemWriterResult},
8 BatchError,
9};
10
11pub struct CsvItemWriter<T: Write> {
12 writer: RefCell<Writer<T>>,
13}
14
15impl<T: Write, R: Serialize> ItemWriter<R> for CsvItemWriter<T> {
16 fn write(&self, items: &[R]) -> ItemWriterResult {
17 for item in items.iter() {
18 let result = self.writer.borrow_mut().serialize(item);
19
20 if result.is_err() {
21 let error = result.err().unwrap();
22 return Err(BatchError::ItemWriter(error.to_string()));
23 }
24 }
25 Ok(())
26 }
27
28 /// Flush the contents of the internal buffer to the underlying writer.
29 ///
30 /// If there was a problem writing to the underlying writer, then an error
31 /// is returned.
32 ///
33 /// Note that this also flushes the underlying writer.
34 fn flush(&self) -> ItemWriterResult {
35 let result = self.writer.borrow_mut().flush();
36 match result {
37 Ok(()) => Ok(()),
38 Err(error) => Err(BatchError::ItemWriter(error.to_string())),
39 }
40 }
41}
42
43#[derive(Default)]
44pub struct CsvItemWriterBuilder {
45 delimiter: u8,
46 has_headers: bool,
47}
48
49impl CsvItemWriterBuilder {
50 pub fn new() -> Self {
51 Self {
52 delimiter: b',',
53 has_headers: false,
54 }
55 }
56
57 pub fn delimiter(mut self, delimiter: u8) -> Self {
58 self.delimiter = delimiter;
59 self
60 }
61
62 pub fn has_headers(mut self, yes: bool) -> Self {
63 self.has_headers = yes;
64 self
65 }
66
67 pub fn from_path<R: AsRef<Path>>(self, path: R) -> CsvItemWriter<File> {
68 let writer = WriterBuilder::new()
69 .flexible(false)
70 .has_headers(self.has_headers)
71 .from_path(path);
72
73 CsvItemWriter {
74 writer: RefCell::new(writer.unwrap()),
75 }
76 }
77
78 /// Serialize a single record using Serde.
79 ///
80 /// # Example
81 ///
82 /// This shows how to serialize normal Rust structs as CSV records. The
83 /// fields of the struct are used to write a header row automatically.
84 /// (Writing the header row automatically can be disabled by building the
85 /// CSV writer with a [`WriterBuilder`](struct.WriterBuilder.html) and
86 /// calling the `has_headers` method.)
87 ///
88 /// ```
89 /// # use std::error::Error;
90 /// # use csv::Writer;
91 /// # use spring_batch_rs::{item::csv::csv_writer::CsvItemWriterBuilder, core::item::ItemWriter};
92 /// #[derive(serde::Serialize)]
93 /// struct Row<'a> {
94 /// city: &'a str,
95 /// country: &'a str,
96 /// #[serde(rename = "popcount")]
97 /// population: u64,
98 /// }
99 ///
100 /// # fn main() { example().unwrap(); }
101 /// fn example() -> Result<(), Box<dyn Error>> {
102 /// let wtr = CsvItemWriterBuilder::new()
103 /// .has_headers(true)
104 /// .from_writer(vec![]);
105 ///
106 /// let rows = &[
107 /// Row {
108 /// city: "Boston",
109 /// country: "United States",
110 /// population: 4628910,
111 /// },
112 /// Row {
113 /// city: "Concord",
114 /// country: "United States",
115 /// population: 42695,
116 /// }
117 /// ];
118 /// wtr.write(rows);
119 ///
120 /// Ok(())
121 /// }
122 /// ```
123 pub fn from_writer<W: Write>(self, wtr: W) -> CsvItemWriter<W> {
124 let wtr = WriterBuilder::new()
125 .flexible(false)
126 .has_headers(self.has_headers)
127 .from_writer(wtr);
128
129 CsvItemWriter {
130 writer: RefCell::new(wtr),
131 }
132 }
133}