1use std::io::{self, Read, Write};
2use std::{
3 fs::OpenOptions,
4 path::{Path, PathBuf},
5};
6
7use crate::{RollbackableOperation, SingleFileOperation};
8
9pub struct WriteFile {
11 source: PathBuf,
12 temp_dir: PathBuf,
13 backup_path: PathBuf,
14 data: Vec<u8>,
15}
16
17impl WriteFile {
18 pub fn new<S: AsRef<Path>, T: AsRef<Path>>(source: S, temp_dir: T, data: Vec<u8>) -> Self {
20 Self {
21 source: source.as_ref().into(),
22 temp_dir: temp_dir.as_ref().into(),
23 backup_path: PathBuf::new(),
24 data: data,
25 }
26 }
27}
28
29impl RollbackableOperation for WriteFile {
30 fn execute(&mut self) -> io::Result<()> {
31 self.create_backup_file()?;
32
33 OpenOptions::new()
34 .write(true)
35 .open(self.get_path())?
36 .write_all(&self.data)
37 }
38
39 fn rollback(&self) -> io::Result<()> {
40 let mut buffer = Vec::<u8>::new();
41 let mut backup_file = OpenOptions::new().read(true).open(self.get_backup_path())?;
42
43 backup_file.read_to_end(&mut buffer)?;
44
45 OpenOptions::new()
46 .write(true)
47 .truncate(true)
48 .open(self.get_path())?
49 .write_all(&buffer)
50 }
51}
52
53impl SingleFileOperation for WriteFile {
54 fn get_path(&self) -> &Path {
55 &self.source
56 }
57
58 fn get_backup_path(&self) -> &Path {
59 &self.backup_path
60 }
61
62 fn set_backup_path<S: AsRef<Path>>(&mut self, uuid: S) {
63 self.backup_path = uuid.as_ref().into();
64 }
65
66 fn get_temp_dir(&self) -> &Path {
67 &self.temp_dir
68 }
69}
70
71impl Drop for WriteFile {
72 fn drop(&mut self) {
73 match self.dispose() {
74 Err(e) => eprintln!("{}", e),
75 _ => {}
76 }
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use std::fs::{self, File};
83
84 use super::*;
85
86 const FILE_SOURCE: &str = "./write_file_source.txt";
87 const TEMP_DIR: &str = "./tmp/";
88 const INITIAL_DATA: &[u8] = "Yellow World".as_bytes();
89 const WRITTEN_DATA: &[u8] = "Hello World".as_bytes();
90
91 fn write_setup() -> std::io::Result<()> {
92 match File::create(FILE_SOURCE) {
93 Ok(_f) => Ok(()),
94 Err(e) => Err(e),
95 }
96 }
97
98 #[test]
99 #[allow(unused_must_use)]
100 fn write_file_works() {
101 assert_eq!((), write_setup().unwrap());
102
103 fs::write(FILE_SOURCE, INITIAL_DATA).expect("Unable to write file");
104
105 let mut op = WriteFile::new(FILE_SOURCE, TEMP_DIR, WRITTEN_DATA.to_vec());
106
107 assert_eq!((), op.execute().unwrap());
108 let data = fs::read_to_string(FILE_SOURCE).expect("Unable to read file");
109 assert_eq!(String::from("Hello Worldd"), data);
110
111 assert_eq!((), op.rollback().unwrap());
112 let data = fs::read_to_string(FILE_SOURCE).expect("Unable to read file");
113 assert_eq!(String::from_utf8(INITIAL_DATA.to_vec()).unwrap(), data);
114
115 fs::remove_file(FILE_SOURCE);
116 }
117}