rpgmxp_tool/commands/compile_assets/
file_sink.rs

1use anyhow::bail;
2use anyhow::Context;
3use std::fs::File;
4use std::io::BufWriter;
5use std::io::Read;
6use std::io::Write;
7use std::path::Path;
8use std::path::PathBuf;
9
10/// An abstraction of a file sink over dir and rgssad output formats.
11#[derive(Debug)]
12pub enum FileSink {
13    Dir {
14        base_path: PathBuf,
15    },
16    Rgssad {
17        writer: rgssad::Writer<BufWriter<File>>,
18    },
19}
20
21impl FileSink {
22    /// Create a new file sink for a directory
23    pub fn new_dir(path: &Path, overwrite: bool) -> anyhow::Result<Self> {
24        if path.try_exists()? {
25            if overwrite {
26                std::fs::remove_dir_all(path)?;
27            } else {
28                bail!("output path exists");
29            }
30        }
31
32        std::fs::create_dir_all(path)
33            .with_context(|| format!("failed to create dir at \"{}\"", path.display()))?;
34
35        // TODO: Maybe use a dir lock?
36
37        Ok(Self::Dir {
38            base_path: path.into(),
39        })
40    }
41
42    /// Create a new file sink for an rgssad file
43    pub fn new_rgssad(path: &Path, overwrite: bool) -> anyhow::Result<Self> {
44        if path.try_exists()? {
45            if overwrite {
46                std::fs::remove_file(path)?;
47            } else {
48                bail!("output path exists");
49            }
50        }
51
52        // TODO: Lock the file?
53
54        let file = File::create_new(path)?;
55        let file = BufWriter::new(file);
56        let mut writer = rgssad::Writer::new(file);
57        writer.write_header()?;
58
59        Ok(Self::Rgssad { writer })
60    }
61
62    /// Write a file.
63    pub fn write_file<R>(
64        &mut self,
65        path_components: &[&str],
66        size: u32,
67        mut reader: R,
68    ) -> anyhow::Result<()>
69    where
70        R: Read,
71    {
72        match self {
73            Self::Dir { base_path } => {
74                let mut path = base_path.clone();
75                path.extend(path_components);
76
77                if let Some(parent_path) = path.parent() {
78                    std::fs::create_dir_all(parent_path)?;
79                }
80
81                // TODO: Temp paths?
82                let mut file = File::create_new(path)?;
83                std::io::copy(&mut reader, &mut file)?;
84                file.flush()?;
85                file.sync_all()?;
86            }
87            Self::Rgssad { writer } => {
88                // Create a windows-style path.
89                let path = path_components.join("\\");
90
91                writer.write_file(&path, size, reader)?;
92            }
93        }
94
95        Ok(())
96    }
97
98    /// Finish and close this file sink.
99    pub fn finish(&mut self) -> anyhow::Result<()> {
100        match self {
101            Self::Dir { .. } => {}
102            Self::Rgssad { writer } => {
103                let buf_writer = writer.get_mut();
104                buf_writer.flush()?;
105
106                let file = buf_writer.get_mut();
107                file.sync_all()?;
108            }
109        }
110
111        Ok(())
112    }
113}