csv_zip_maker/
csv_zip_maker.rs1use std::{
2 fs::File,
3 io::{BufReader, BufWriter, Read, Write},
4 path::{Path, PathBuf},
5};
6
7use csv::WriterBuilder;
8use tempfile::TempDir;
9use zip::{write::FileOptions, ZipWriter};
10
11use crate::{csv_maker::CsvMaker, CsvCustomizer, CsvExcelCustomizer, CsvZipError};
12
13pub struct CsvZipMaker {
14 tempdir: TempDir,
15 writer: ZipWriter<BufWriter<File>>,
16 file_path: PathBuf,
17 file_options: FileOptions,
18}
19
20impl CsvZipMaker {
21 pub fn new(prefix: &str, name: &str) -> Result<Self, CsvZipError> {
22 Self::new_with_file_option(prefix, name, FileOptions::default())
23 }
24
25 pub fn new_with_file_option(
26 prefix: &str,
27 name: &str,
28 file_options: FileOptions,
29 ) -> Result<Self, CsvZipError> {
30 let tempdir = TempDir::with_prefix(prefix)?;
31 let file_path = tempdir.path().join(format!("{}.zip", name));
32 let buf_writer = BufWriter::new(File::create(&file_path)?);
33 let writer = ZipWriter::new(buf_writer);
34 Ok(Self {
35 tempdir,
36 writer,
37 file_path,
38 file_options,
39 })
40 }
41
42 pub fn make_csv_maker(&self, name: &str) -> Result<CsvMaker, CsvZipError> {
43 self.make_csv_maker_with_customizer(name, ())
44 }
45
46 pub fn make_csv_maker_for_excel(&self, name: &str) -> Result<CsvMaker, CsvZipError> {
47 self.make_csv_maker_with_customizer(name, CsvExcelCustomizer)
48 }
49
50 pub fn make_csv_maker_with_customizer(
51 &self,
52 name: &str,
53 customizer: impl CsvCustomizer,
54 ) -> Result<CsvMaker, CsvZipError> {
55 let file_name = format!("{}.csv", name);
56 let file_path = self.tempdir.path().join(&file_name);
57 let mut buf_writer = BufWriter::new(File::create(&file_path)?);
58 let mut writer_builder = WriterBuilder::new();
59
60 customizer.customize(&mut buf_writer, &mut writer_builder)?;
61
62 Ok(CsvMaker {
63 writer: writer_builder.from_writer(buf_writer),
64 file_name,
65 file_path,
66 })
67 }
68
69 fn execute_csv(&mut self, file_name: &str, file_path: &PathBuf) -> Result<(), CsvZipError> {
70 self.writer.start_file(file_name, self.file_options)?;
71 let mut f = BufReader::new(File::open(file_path)?);
72 let mut buf = [0; 1024];
73 loop {
74 match f.read(&mut buf)? {
75 0 => break,
76 n => {
77 self.writer.write_all(&buf[0..n])?;
78 }
79 }
80 }
81 Ok(())
82 }
83
84 pub fn add_csv(&mut self, csv_maker: &mut CsvMaker) -> Result<(), CsvZipError> {
85 csv_maker.flush()?;
86 self.execute_csv(&csv_maker.file_name, &csv_maker.file_path)
87 }
88
89 pub fn add_csv_utf16(&mut self, csv_maker: &mut CsvMaker) -> Result<(), CsvZipError> {
90 csv_maker.flush()?;
91 let writer_file_path = self.tempdir.path().join("utf16.csv");
92 convert_file(&csv_maker.file_path, &writer_file_path)?;
93 self.execute_csv(&csv_maker.file_name, &writer_file_path)
94 }
95
96 pub fn make_zip_file(&mut self) -> Result<&PathBuf, CsvZipError> {
97 let mut res = self.writer.finish()?;
98 res.flush()?;
99 Ok(&self.file_path)
100 }
101
102 pub fn make_zip_binary(&mut self) -> Result<Vec<u8>, CsvZipError> {
103 let mut res = self.writer.finish()?;
104 res.flush()?;
105 let mut file = File::open(&self.file_path)?;
106 let mut buf = Vec::new();
107 let _ = file.read_to_end(&mut buf)?;
108 Ok(buf)
109 }
110}
111
112const BYTE_ORDER_MARK: [u8; 2] = [0xFF, 0xFE];
113const CR: u8 = b'\r';
114const LF: u8 = b'\n';
115
116fn convert_file<P>(src: P, dst: P) -> Result<(), CsvZipError>
117where
118 P: AsRef<Path>,
119{
120 let mut reader = BufReader::new(File::open(src)?);
121 let mut writer = BufWriter::new(File::create(dst)?);
122
123 writer.write_all(&BYTE_ORDER_MARK)?;
125
126 let mut buf = [0; 1];
128 let mut buffer: Vec<u8> = Vec::new();
129 let mut cr_flag = false;
130 loop {
131 match reader.read(&mut buf)? {
132 0 => break, _n => {
134 buffer.push(buf[0]);
135 if cr_flag {
136 if buf[0] == LF {
137 writer.write_all(&make_bytes(buffer)?)?;
139 buffer = Vec::new();
140 cr_flag = false;
141 } else if buf[0] == CR {
142 } else {
144 cr_flag = false;
146 }
147 } else if buf[0] == CR {
148 cr_flag = true;
150 }
151 }
152 }
153 }
154 writer.flush().map_err(|e| e.into())
155}
156
157fn make_bytes(src: Vec<u8>) -> Result<Vec<u8>, CsvZipError> {
158 let src = match String::from_utf8(src) {
159 Ok(res) => res,
160 Err(e) => return Err(CsvZipError::Utf16(e.to_string())),
161 };
162 let dst: Vec<u8> = src.encode_utf16().flat_map(|it| it.to_le_bytes()).collect();
163 Ok(dst)
164}