comtrya_lib/atoms/file/
copy.rs1use 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}