rpgmv_tool/command/commands2py/
file_sink.rs

1use anyhow::ensure;
2use anyhow::Context;
3use std::fs::File;
4use std::io::BufWriter;
5use std::path::Path;
6use std::path::PathBuf;
7
8/// A destination for bytes.
9#[derive(Debug)]
10pub enum FileSink {
11    File {
12        path: PathBuf,
13        path_temp: PathBuf,
14        file: BufWriter<File>,
15    },
16    Empty,
17}
18
19impl FileSink {
20    /// Make a new [`FileSink`].
21    pub fn new<P>(path: P, dry_run: bool, overwrite: bool) -> anyhow::Result<Self>
22    where
23        P: AsRef<Path>,
24    {
25        let path = path.as_ref();
26        ensure!(
27            overwrite || !path.try_exists()?,
28            "output path \"{}\" already exists. Use the --overwrite flag to overwrite",
29            path.display()
30        );
31
32        if dry_run {
33            Ok(FileSink::new_empty())
34        } else {
35            FileSink::new_file(path)
36        }
37    }
38
39    /// Create a new file variant.
40    fn new_file<P>(path: P) -> anyhow::Result<Self>
41    where
42        P: AsRef<Path>,
43    {
44        let path = path.as_ref();
45        let path_temp = nd_util::with_push_extension(path, "tmp");
46        let file = File::create(&path_temp)
47            .with_context(|| format!("failed to open \"{}\"", path_temp.display()))?;
48        let file = BufWriter::new(file);
49
50        Ok(Self::File {
51            path: path.to_path_buf(),
52            path_temp,
53            file,
54        })
55    }
56
57    /// Create a new empty variant.
58    fn new_empty() -> Self {
59        Self::Empty
60    }
61
62    /// Finish using this file sink, writing the result.
63    pub fn finish(self) -> anyhow::Result<()> {
64        match self {
65            Self::File {
66                path,
67                path_temp,
68                file,
69            } => {
70                let file = file.into_inner()?;
71                file.sync_all()?;
72
73                std::fs::rename(&path_temp, path)?;
74
75                Ok(())
76            }
77            Self::Empty => Ok(()),
78        }
79    }
80}
81
82impl std::io::Write for FileSink {
83    fn write(&mut self, buffer: &[u8]) -> std::io::Result<usize> {
84        match self {
85            Self::File { file, .. } => file.write(buffer),
86            Self::Empty => Ok(buffer.len()),
87        }
88    }
89
90    fn flush(&mut self) -> std::io::Result<()> {
91        match self {
92            Self::File { file, .. } => file.flush(),
93            Self::Empty => Ok(()),
94        }
95    }
96}