csv_zip_maker/
csv_zip_maker.rs

1use 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    // BOMを書き込む
124    writer.write_all(&BYTE_ORDER_MARK)?;
125
126    // 1byteごとに取得
127    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, // 行の最後はCRLFでおわるはず。
133            _n => {
134                buffer.push(buf[0]);
135                if cr_flag {
136                    if buf[0] == LF {
137                        // CRLFが完成した
138                        writer.write_all(&make_bytes(buffer)?)?;
139                        buffer = Vec::new();
140                        cr_flag = false;
141                    } else if buf[0] == CR {
142                        // 連続でCRがきた場合はcr_flagは立てたまま
143                    } else {
144                        // CRの次にCRまたはLFが来ていない場合はCRフラグを落とす
145                        cr_flag = false;
146                    }
147                } else if buf[0] == CR {
148                    // CRが来たのでフラグを立てる
149                    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}