comtrya_lib/atoms/file/
copy.rs

1use crate::atoms::Outcome;
2
3use super::super::Atom;
4use super::FileAtom;
5use file_diff::diff;
6use std::path::PathBuf;
7use tracing::error;
8
9pub struct Copy {
10    pub from: PathBuf,
11    pub to: PathBuf,
12}
13
14impl FileAtom for Copy {
15    fn get_path(&self) -> &PathBuf {
16        &self.from
17    }
18}
19
20impl std::fmt::Display for Copy {
21    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22        write!(
23            f,
24            "The file {} contents needs to be copied from {}",
25            self.to.display(),
26            self.from.display(),
27        )
28    }
29}
30
31impl Atom for Copy {
32    fn plan(&self) -> anyhow::Result<Outcome> {
33        if !self.to.is_file() {
34            error!("Cannot plan: target isn't a file: {}", self.to.display());
35
36            return Ok(Outcome {
37                side_effects: vec![],
38                should_run: false,
39            });
40        }
41
42        return Ok(Outcome {
43            side_effects: vec![],
44            should_run: !diff(
45                &self.from.display().to_string(),
46                &self.to.display().to_string(),
47            ),
48        });
49    }
50
51    fn execute(&mut self) -> anyhow::Result<()> {
52        std::fs::copy(&self.from, &self.to)?;
53
54        Ok(())
55    }
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61    use pretty_assertions::assert_eq;
62    use std::io::Write;
63
64    #[test]
65    fn it_can_plan() {
66        let to_file = match tempfile::NamedTempFile::new() {
67            std::result::Result::Ok(file) => file,
68            std::result::Result::Err(_) => {
69                assert_eq!(false, true);
70                return;
71            }
72        };
73
74        let mut from_file = match tempfile::NamedTempFile::new() {
75            std::result::Result::Ok(file) => file,
76            std::result::Result::Err(_) => {
77                assert_eq!(false, true);
78                return;
79            }
80        };
81
82        assert_eq!(
83            true,
84            writeln!(
85                from_file.as_file_mut(),
86                "This is test content for a test file"
87            )
88            .is_ok()
89        );
90
91        let file_copy = Copy {
92            from: from_file.path().to_path_buf(),
93            to: to_file.path().to_path_buf(),
94        };
95
96        assert_eq!(true, file_copy.plan().unwrap().should_run);
97
98        let file_copy = Copy {
99            from: from_file.path().to_path_buf(),
100            to: from_file.path().to_path_buf(),
101        };
102
103        assert_eq!(false, file_copy.plan().unwrap().should_run);
104    }
105
106    #[test]
107    fn it_can_execute() {
108        use std::io::Write;
109
110        let to_file = match tempfile::NamedTempFile::new() {
111            std::result::Result::Ok(file) => file,
112            std::result::Result::Err(_) => {
113                assert_eq!(false, true);
114                return;
115            }
116        };
117
118        let mut from_file = match tempfile::NamedTempFile::new() {
119            std::result::Result::Ok(file) => file,
120            std::result::Result::Err(_) => {
121                assert_eq!(false, true);
122                return;
123            }
124        };
125
126        assert_eq!(
127            true,
128            writeln!(
129                from_file.as_file_mut(),
130                "This is test content for a test file"
131            )
132            .is_ok()
133        );
134
135        let mut file_copy = Copy {
136            from: from_file.path().to_path_buf(),
137            to: to_file.path().to_path_buf(),
138        };
139
140        assert_eq!(true, file_copy.plan().unwrap().should_run);
141        assert_eq!(true, file_copy.execute().is_ok());
142        assert_eq!(false, file_copy.plan().unwrap().should_run);
143    }
144
145    #[test]
146    fn it_wont_destroy_directories() {
147        let to = match tempfile::TempDir::new() {
148            std::result::Result::Ok(dir) => dir,
149            std::result::Result::Err(_) => {
150                assert_eq!(false, true);
151                return;
152            }
153        };
154
155        let from_file = match tempfile::NamedTempFile::new() {
156            std::result::Result::Ok(file) => file,
157            std::result::Result::Err(_) => {
158                assert_eq!(false, true);
159                return;
160            }
161        };
162
163        let file_copy = Copy {
164            from: from_file.path().to_path_buf(),
165            to: to.path().to_path_buf(),
166        };
167
168        assert_eq!(false, file_copy.plan().unwrap().should_run);
169    }
170}