1use std::path::{Path, PathBuf};
2
3use super::Time;
4
5use crate::pre::*;
6
7pub fn current_exe() -> crate::Result<PathBuf> {
17 std::env::current_exe().context("failed to get current exe path")
18}
19
20#[inline(always)]
28pub fn copy(from: impl AsRef<Path>, to: impl AsRef<Path>) -> crate::Result<u64> {
29 copy_impl(from.as_ref(), to.as_ref())
30}
31fn copy_impl(from: &Path, to: &Path) -> crate::Result<u64> {
32 crate::trace!("copy from='{}' to='{}'", from.display(), to.display());
33 if to.is_dir() {
34 crate::bail!(
35 "cannot copy from '{}' to '{}': target is directory",
36 from.display(),
37 to.display()
38 );
39 }
40 let copy_error = match std::fs::copy(from, to) {
41 Ok(v) => return Ok(v),
42 Err(e) => e,
43 };
44 if !from.exists() {
45 crate::rethrow!(
46 copy_error,
47 "cannot copy from '{}' to '{}': source does not exist",
48 from.display(),
49 to.display()
50 );
51 }
52 if from.is_dir() {
53 crate::rethrow!(
54 copy_error,
55 "cannot copy from '{}' to '{}': source is directory",
56 from.display(),
57 to.display()
58 );
59 }
60 if copy_error.kind() == std::io::ErrorKind::NotFound {
61 if let Ok(parent) = to.parent_abs()
63 && !parent.exists()
64 {
65 crate::trace!(
66 "retrying with parent creation: copy from='{}' to='{}'",
67 from.display(),
68 to.display()
69 );
70 crate::check!(
71 super::make_dir(&parent),
72 "could not automatically create parent directory for '{}'",
73 to.display()
74 )?;
75 return crate::check!(
77 std::fs::copy(from, to),
78 "failed to copy file from '{}' to '{}'",
79 from.display(),
80 to.display()
81 );
82 }
83 }
84 crate::rethrow!(
85 copy_error,
86 "failed to copy file from '{}' to '{}'",
87 from.display(),
88 to.display()
89 );
90}
91
92#[inline(always)]
96pub fn get_mtime(path: impl AsRef<Path>) -> crate::Result<Option<Time>> {
97 get_mtime_impl(path.as_ref())
98}
99fn get_mtime_impl(path: &Path) -> crate::Result<Option<Time>> {
100 match path.metadata() {
101 Ok(meta) => Ok(Some(Time::from_last_modification_time(&meta))),
102 Err(e) => {
103 if !path.exists() {
104 return Ok(None);
105 }
106 crate::rethrow!(
107 e,
108 "failed to get modification time for '{}'",
109 path.display()
110 );
111 }
112 }
113}
114
115#[inline(always)]
117pub fn set_mtime(path: impl AsRef<Path>, time: Time) -> crate::Result<()> {
118 set_mtime_impl(path.as_ref(), time)
119}
120fn set_mtime_impl(path: &Path, time: Time) -> crate::Result<()> {
121 crate::check!(
122 filetime::set_file_mtime(path, time),
123 "failed to set modification time for '{}'",
124 path.display()
125 )
126}
127
128#[inline(always)]
133pub fn remove(path: impl AsRef<Path>) -> crate::Result<()> {
134 remove_impl(path.as_ref())
135}
136fn remove_impl(path: &Path) -> crate::Result<()> {
137 if !path.exists() {
138 crate::trace!("remove: is absent: '{}'", path.display());
139 return Ok(());
140 }
141 crate::trace!("remove '{}'", path.display());
142 if path.is_dir() {
143 return crate::check!(
144 std::fs::remove_dir(path),
145 "failed to remove directory '{}'",
146 path.display()
147 );
148 }
149 crate::check!(
150 std::fs::remove_file(path),
151 "failed to remove file '{}'",
152 path.display()
153 )
154}
155
156#[cfg(feature = "coroutine")]
158#[inline(always)]
159pub async fn co_remove(path: impl AsRef<Path>) -> crate::Result<()> {
160 co_remove_impl(path.as_ref()).await
161}
162#[cfg(feature = "coroutine")]
163async fn co_remove_impl(path: &Path) -> crate::Result<()> {
164 if !path.exists() {
165 crate::trace!("co_remove: is absent: '{}'", path.display());
166 return Ok(());
167 }
168 crate::trace!("co_remove '{}'", path.display());
169 if path.is_dir() {
170 return crate::check!(
171 tokio::fs::remove_dir(path).await,
172 "failed to remove directory '{}'",
173 path.display()
174 );
175 }
176 crate::check!(
177 tokio::fs::remove_file(path).await,
178 "failed to remove file '{}'",
179 path.display()
180 )
181}
182
183#[inline(always)]
198pub fn rename(from: impl AsRef<Path>, to: impl AsRef<Path>) -> crate::Result<()> {
199 rename_impl(from.as_ref(), to.as_ref())
200}
201#[crate::context("failed to rename '{}' to '{}'", from.display(), to.display())]
202fn rename_impl(from: &Path, to: &Path) -> crate::Result<()> {
203 crate::trace!("rename: '{}' to '{}'", from.display(), to.display());
204 let Ok(from_meta) = from.metadata() else {
205 crate::bail!("source does not exist");
206 };
207 if from_meta.is_file() {
208 if to.is_dir() {
209 crate::bail!("target is a directory");
210 }
211 let Err(e) = std::fs::rename(from, to) else {
212 return Ok(());
213 };
214 crate::trace!("rename failed: {e}, trying fallback");
215 crate::fs::copy(from, to)?;
217 crate::fs::remove(from)?;
218 return Ok(());
219 }
220 match to.metadata().ok() {
221 None => {
222 }
224 Some(to_meta) => {
225 if !to_meta.is_dir() {
226 crate::bail!("target is not a directory");
228 }
229 if !crate::fs::is_empty_dir(to)? {
230 crate::bail!("target is a non-empty directory");
231 }
232 crate::fs::remove(to)?;
234 }
235 }
236 std::fs::rename(from, to)?;
238 Ok(())
239}