1use std::path::Path;
2
3#[cfg(unix)]
4use std::os::unix::fs::PermissionsExt;
5
6use miette::Diagnostic;
7use thiserror::Error;
8
9pub mod sync;
10pub mod tempfile;
11pub mod tokio;
12#[cfg(unix)]
13pub mod unix;
14#[cfg(windows)]
15pub mod windows;
16
17pub type Result<T> = core::result::Result<T, Error>;
18
19#[derive(Debug, Error, Diagnostic)]
20#[non_exhaustive]
21pub enum Error {
22 #[error("failed to read file '{}'", path.display())]
23 #[diagnostic(
24 code(lux_lib::fs::read),
25 help("ensure '{}' exists and is readable", path.display())
26 )]
27 Read {
28 path: std::path::PathBuf,
29 source: std::io::Error,
30 },
31 #[error("failed to read '{}' as UTF-8 string", path.display())]
32 #[diagnostic(
33 code(lux_lib::fs::read_to_string),
34 help("ensure '{}' exists, is readable, and contains valid UTF-8", path.display())
35 )]
36 ReadToString {
37 path: std::path::PathBuf,
38 source: std::io::Error,
39 },
40 #[error("failed to write to '{}'", path.display())]
41 #[diagnostic(
42 code(lux_lib::fs::write),
43 help("ensure the parent directory of '{}' exists and is writable", path.display())
44 )]
45 Write {
46 path: std::path::PathBuf,
47 source: std::io::Error,
48 },
49 #[error("failed to copy '{}' to '{}'", from.display(), to.display())]
50 #[diagnostic(
51 code(lux_lib::fs::copy),
52 help("ensure '{}' exists and the parent directory of '{}' is writable", from.display(), to.display())
53 )]
54 Copy {
55 from: std::path::PathBuf,
56 to: std::path::PathBuf,
57 source: std::io::Error,
58 },
59 #[error("failed to create directory '{}'", path.display())]
60 #[diagnostic(
61 code(lux_lib::fs::create_dir),
62 help("ensure the parent directory of '{}' exists and is writable", path.display())
63 )]
64 CreateDir {
65 path: std::path::PathBuf,
66 source: std::io::Error,
67 },
68 #[error("failed to create a temporaty directory")]
69 #[diagnostic(
70 code(lux_lib::fs::create_tempdir),
71 help("ensure the '{}' exists and is writable", std::env::temp_dir().display())
72 )]
73 CreateTempDir { source: std::io::Error },
74 #[error("failed to create directory tree '{}'", path.display())]
75 #[diagnostic(
76 code(lux_lib::fs::create_dir_all),
77 help("ensure the path to '{}' is accessible", path.display())
78 )]
79 CreateDirAll {
80 path: std::path::PathBuf,
81 source: std::io::Error,
82 },
83 #[error("failed to remove file '{}'", path.display())]
84 #[diagnostic(
85 code(lux_lib::fs::remove_file),
86 help("ensure '{}' exists and is writable", path.display())
87 )]
88 RemoveFile {
89 path: std::path::PathBuf,
90 source: std::io::Error,
91 },
92 #[error("failed to remove directory '{}'", path.display())]
93 #[diagnostic(
94 code(lux_lib::fs::remove_dir_all),
95 help("ensure '{}' exists and is writable", path.display())
96 )]
97 RemoveDirAll {
98 path: std::path::PathBuf,
99 source: std::io::Error,
100 },
101 #[error("failed to read directory '{}'", path.display())]
102 #[diagnostic(
103 code(lux_lib::fs::read_dir),
104 help("ensure '{}' exists and is a directory", path.display())
105 )]
106 ReadDir {
107 path: std::path::PathBuf,
108 source: std::io::Error,
109 },
110 #[error("failed to get metadata for '{}'", path.display())]
111 #[diagnostic(
112 code(lux_lib::fs::metadata),
113 help("ensure '{}' exists", path.display())
114 )]
115 Metadata {
116 path: std::path::PathBuf,
117 source: std::io::Error,
118 },
119 #[error("failed to rename '{}' to '{}'", from.display(), to.display())]
120 #[diagnostic(
121 code(lux_lib::fs::rename),
122 help("ensure '{}' exists and the parent directory of '{}' is writable", from.display(), to.display())
123 )]
124 Rename {
125 from: std::path::PathBuf,
126 to: std::path::PathBuf,
127 source: std::io::Error,
128 },
129 #[error("failed to set permissions on '{}'", path.display())]
130 #[diagnostic(
131 code(lux_lib::fs::set_permissions),
132 help("ensure '{}' exists and is writable", path.display())
133 )]
134 SetPermissions {
135 path: std::path::PathBuf,
136 source: std::io::Error,
137 },
138 #[error("failed to open file '{}'", path.display())]
139 #[diagnostic(
140 code(lux_lib::fs::file_open),
141 help("ensure '{}' exists and is readable", path.display())
142 )]
143 FileOpen {
144 path: std::path::PathBuf,
145 source: std::io::Error,
146 },
147 #[error("failed to create file '{}'", path.display())]
148 #[diagnostic(
149 code(lux_lib::fs::file_create),
150 help("ensure the parent directory of '{}' exists and is writable", path.display())
151 )]
152 FileCreate {
153 path: std::path::PathBuf,
154 source: std::io::Error,
155 },
156 #[error("failed to get the absolute path for '{}'", path.display())]
157 #[diagnostic(code(lux_lib::fs::get_absolute_path))]
158 GetAbsolutePath {
159 path: std::path::PathBuf,
160 source: std::io::Error,
161 },
162 #[error("failed to create symlink '{}' pointing to '{}'", link.display(), target.display())]
163 #[diagnostic(
164 code(lux_lib::fs::symlink),
165 help("ensure the parent directory of '{}' exists and is writable", link.display())
166 )]
167 Symlink {
168 target: std::path::PathBuf,
169 link: std::path::PathBuf,
170 source: std::io::Error,
171 },
172 #[error("failed to create hard link '{}' pointing to '{}'", to.display(), from.display())]
173 #[diagnostic(
174 code(lux_lib::fs::hard_link),
175 help("ensure '{}' is on a filesystem that supports hard links", to.display())
176 )]
177 HardLink {
178 from: std::path::PathBuf,
179 to: std::path::PathBuf,
180 source: std::io::Error,
181 },
182 #[error("{0}")]
183 Other(String),
184}
185
186#[cfg(unix)]
190pub(crate) async fn make_writable(path: &Path) -> Result<()> {
191 let mut perms = self::tokio::metadata(path).await?.permissions();
192 perms.set_mode(perms.mode() | 0o200);
193 self::tokio::set_permissions(path, perms).await?;
194 Ok(())
195}
196
197#[cfg(not(unix))]
198pub(crate) async fn make_writable(_path: &Path) -> Result<()> {
199 Ok(())
200}
201
202#[cfg(unix)]
204pub(crate) fn make_writable_sync(path: &Path) -> Result<()> {
205 let mut perms = std::fs::metadata(path)
206 .map_err(|source| Error::Metadata {
207 path: path.to_path_buf(),
208 source,
209 })?
210 .permissions();
211 perms.set_mode(perms.mode() | 0o200);
212 std::fs::set_permissions(path, perms).map_err(|source| Error::SetPermissions {
213 path: path.to_path_buf(),
214 source,
215 })?;
216 Ok(())
217}
218
219#[cfg(not(unix))]
220pub(crate) fn make_writable_sync(_path: &Path) -> Result<()> {
221 Ok(())
222}
223
224#[cfg(unix)]
225#[tracing::instrument(level = "trace")]
226pub(crate) async fn set_executable_permissions(script: &Path) -> Result<()> {
227 let mut perms = self::tokio::metadata(script).await?.permissions();
228 perms.set_mode(perms.mode() | 0o111);
229 self::tokio::set_permissions(script, perms).await?;
230 Ok(())
231}