comtrya_lib/atoms/file/
chmod.rs

1use crate::atoms::Outcome;
2
3use super::super::Atom;
4use super::FileAtom;
5use std::path::PathBuf;
6
7pub struct Chmod {
8    pub path: PathBuf,
9    pub mode: u32,
10}
11
12impl FileAtom for Chmod {
13    fn get_path(&self) -> &PathBuf {
14        &self.path
15    }
16}
17
18impl std::fmt::Display for Chmod {
19    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20        write!(
21            f,
22            "The permissions on {} need to be set to {:o}",
23            self.path.display(),
24            self.mode
25        )
26    }
27}
28
29#[cfg(unix)]
30use {std::os::unix::prelude::PermissionsExt, tracing::error};
31
32#[cfg(unix)]
33impl Atom for Chmod {
34    fn plan(&self) -> anyhow::Result<Outcome> {
35        // If the file doesn't exist, assume it's because
36        // another atom is going to provide it.
37        if !self.path.exists() {
38            return Ok(Outcome {
39                side_effects: vec![],
40                should_run: true,
41            });
42        }
43
44        let metadata = match std::fs::metadata(&self.path) {
45            Ok(m) => m,
46            Err(err) => {
47                error!(
48                    "Couldn't get metadata for {}, rejecting atom: {}",
49                    &self.path.display(),
50                    err.to_string()
51                );
52
53                return Ok(Outcome {
54                    side_effects: vec![],
55                    should_run: false,
56                });
57            }
58        };
59
60        // We expect permissions to come through as if the user was using chmod themselves.
61        // This means we support 644/755 decimal syntax. We need to add 0o100000 to support
62        // the part of chmod they don't often type.
63        Ok(Outcome {
64            side_effects: vec![],
65            should_run: std::fs::Permissions::from_mode(0o100000 + self.mode).mode()
66                != metadata.permissions().mode(),
67        })
68    }
69
70    fn execute(&mut self) -> anyhow::Result<()> {
71        std::fs::set_permissions(
72            self.path.as_path(),
73            std::fs::Permissions::from_mode(self.mode),
74        )?;
75
76        Ok(())
77    }
78}
79
80#[cfg(not(unix))]
81impl Atom for Chmod {
82    fn plan(&self) -> anyhow::Result<Outcome> {
83        // Never run
84        Ok(Outcome {
85            side_effects: vec![],
86            should_run: false,
87        })
88    }
89
90    fn execute(&mut self) -> anyhow::Result<()> {
91        Ok(())
92    }
93}
94
95#[cfg(test)]
96#[cfg(unix)]
97mod tests {
98    use super::*;
99    use pretty_assertions::assert_eq;
100
101    #[test]
102    fn it_can_plan() {
103        let temp_dir = match tempfile::tempdir() {
104            std::result::Result::Ok(dir) => dir,
105            std::result::Result::Err(_) => {
106                assert_eq!(false, true);
107                return;
108            }
109        };
110
111        match std::fs::File::create(temp_dir.path().join("644")) {
112            std::result::Result::Ok(file) => file,
113            std::result::Result::Err(_) => {
114                assert_eq!(false, true);
115                return;
116            }
117        };
118
119        assert_eq!(
120            true,
121            std::fs::set_permissions(
122                temp_dir.path().join("644"),
123                std::fs::Permissions::from_mode(0o644)
124            )
125            .is_ok(),
126        );
127
128        let file_chmod = Chmod {
129            path: temp_dir.path().join("644"),
130            mode: 0o644,
131        };
132
133        assert_eq!(false, file_chmod.plan().unwrap().should_run);
134
135        let file_chmod = Chmod {
136            path: temp_dir.path().join("644"),
137            mode: 0o640,
138        };
139
140        assert_eq!(true, file_chmod.plan().unwrap().should_run);
141    }
142
143    #[test]
144    fn it_can_execute() {
145        let temp_dir = match tempfile::tempdir() {
146            std::result::Result::Ok(dir) => dir,
147            std::result::Result::Err(_) => {
148                assert_eq!(false, true);
149                return;
150            }
151        };
152
153        match std::fs::File::create(temp_dir.path().join("644")) {
154            std::result::Result::Ok(file) => file,
155            std::result::Result::Err(_) => {
156                assert_eq!(false, true);
157                return;
158            }
159        };
160
161        assert_eq!(
162            true,
163            std::fs::set_permissions(
164                temp_dir.path().join("644"),
165                std::fs::Permissions::from_mode(0o644)
166            )
167            .is_ok(),
168        );
169
170        let file_chmod = Chmod {
171            path: temp_dir.path().join("644"),
172            mode: 0o644,
173        };
174
175        assert_eq!(false, file_chmod.plan().unwrap().should_run);
176
177        let mut file_chmod = Chmod {
178            path: temp_dir.path().join("644"),
179            mode: 0o640,
180        };
181
182        assert_eq!(true, file_chmod.plan().unwrap().should_run);
183        assert_eq!(true, file_chmod.execute().is_ok());
184        assert_eq!(false, file_chmod.plan().unwrap().should_run);
185    }
186}