organize_rs_core/
filesystem.rs

1//! filesystem related
2
3// #[cfg(unix)]
4// use std::os::unix::fs::symlink;
5
6use std::{
7    fs::{remove_dir, remove_file, rename},
8    path::Path,
9};
10
11fn already_exists<A>(dst: &A) -> std::io::Result<()>
12where
13    A: AsRef<Path>,
14{
15    if !dst.as_ref().exists() {
16        Ok(())
17    } else {
18        Err(std::io::Error::new(
19            std::io::ErrorKind::AlreadyExists,
20            format!("file already exists: {}", dst.as_ref().display()),
21        ))
22    }
23}
24
25pub(crate) fn move_to_trash<A>(src: A) -> std::io::Result<()>
26where
27    A: AsRef<Path>,
28{
29    let src_type = src.as_ref().metadata()?.file_type();
30
31    if src_type.is_file() {
32        if let Err(err) = trash::delete(src.as_ref()) {
33            return Err(std::io::Error::new(
34                std::io::ErrorKind::Other,
35                format!("can not trash file: {}, {err}", src.as_ref().display()),
36            ));
37        };
38    } else if src_type.is_dir() {
39        if let Err(err) = trash::delete_all(src.as_ref()) {
40            return Err(std::io::Error::new(
41                std::io::ErrorKind::Other,
42                format!("can not trash directory: {}, {err}", src.as_ref().display()),
43            ));
44        };
45    } else if src_type.is_symlink() {
46        if let Err(err) = trash::delete(src.as_ref()) {
47            return Err(std::io::Error::new(
48                std::io::ErrorKind::Other,
49                format!("can not trash symlink: {}, {err}", src.as_ref().display()),
50            ));
51        };
52    } else {
53        return Err(std::io::Error::new(
54            std::io::ErrorKind::Other,
55            format!("can not trash: {}", src.as_ref().display()),
56        ));
57    }
58    Ok(())
59}
60
61pub(crate) fn remove_irrecoverably<A>(src: A) -> std::io::Result<()>
62where
63    A: AsRef<Path>,
64{
65    let src_type = src.as_ref().metadata()?.file_type();
66
67    if src_type.is_file() {
68        remove_file(src)?;
69    } else if src_type.is_dir() {
70        // TODO: How to treat `remove_dir_all()`,
71        // TODO e.g. if `dir` still has children
72        remove_dir(src)?;
73    } else if src_type.is_symlink() {
74        if let Err(err) = trash::delete(src.as_ref()) {
75            return Err(std::io::Error::new(
76                std::io::ErrorKind::Other,
77                format!("can not remove symlink: {}, {err}", src.as_ref().display()),
78            ));
79        };
80    } else {
81        return Err(std::io::Error::new(
82            std::io::ErrorKind::Other,
83            format!("can not remove: {}", src.as_ref().display()),
84        ));
85    }
86    Ok(())
87}
88
89/// wrapper around rename_to for convenience and clarity
90pub(crate) fn move_to<A, D>(src: A, dst: D) -> std::io::Result<()>
91where
92    A: AsRef<Path>,
93    D: AsRef<Path>,
94{
95    rename_to(src, dst)
96}
97
98pub(crate) fn rename_to<A, D>(src: A, dst: D) -> std::io::Result<()>
99where
100    A: AsRef<Path>,
101    D: AsRef<Path>,
102{
103    // TODO: Conflict handling here
104    already_exists(&dst)?;
105
106    let src_type = src.as_ref().metadata()?.file_type();
107
108    if src_type.is_file() {
109        rename(src, dst)?;
110    } else {
111        return Err(std::io::Error::new(
112            std::io::ErrorKind::Other,
113            format!("can not move/rename: {}", src.as_ref().display()),
114        ));
115    }
116    Ok(())
117}
118
119pub(crate) fn symlink_to<A, D>(src: A, dst: D) -> std::io::Result<()>
120where
121    A: AsRef<Path>,
122    D: AsRef<Path>,
123{
124    // TODO: Conflict handling here
125    already_exists(&dst)?;
126
127    let src_type = src.as_ref().metadata()?.file_type();
128
129    if src_type.is_file() {
130        symlink_file(src, dst)?;
131    } else if src_type.is_dir() {
132        symlink_dir(src, dst)?;
133    } else {
134        return Err(std::io::Error::new(
135            std::io::ErrorKind::Other,
136            format!("can not symlink: {}", src.as_ref().display()),
137        ));
138    }
139    Ok(())
140}
141
142pub(crate) fn copy_to<A, D>(src: A, dst: D) -> std::io::Result<()>
143where
144    A: AsRef<Path>,
145    D: AsRef<Path>,
146{
147    // TODO: Conflict handling here
148    already_exists(&dst)?;
149
150    let src_type = src.as_ref().metadata()?.file_type();
151
152    if src_type.is_file() {
153        std::fs::copy(src, dst)?;
154    } else if src_type.is_dir() {
155        copy_dir_to(src, dst)?;
156    } else {
157        return Err(std::io::Error::new(
158            std::io::ErrorKind::Other,
159            format!("can not deal with file_type: {}", src.as_ref().display()),
160        ));
161    }
162    Ok(())
163}
164
165/// Copy the existing directory `src` to the target path `dst`
166pub(crate) fn copy_dir_to<A, D>(src: A, dst: D) -> std::io::Result<()>
167where
168    A: AsRef<Path>,
169    D: AsRef<Path>,
170{
171    // TODO: Conflict handling here
172    already_exists(&dst)?;
173
174    if !dst.as_ref().is_dir() {
175        std::fs::create_dir(dst.as_ref())?;
176    }
177
178    for entry_result in src.as_ref().read_dir()? {
179        let entry = entry_result?;
180        copy_to(&entry.path(), &dst.as_ref().join(entry.file_name()))?;
181    }
182
183    Ok(())
184}
185
186/// Create a directory symlink to the given src with the given link name.
187/// taken from: https://github.com/Byron/jwalk/blob/0079deb9faed6be48e77676494351f06411db5de/tests/util/mod.rs#L174
188/// Copyright (c) 2019 Jesse Grosjean
189pub fn symlink_dir<A, D>(src: A, dst: D) -> std::io::Result<()>
190where
191    A: AsRef<Path>,
192    D: AsRef<Path>,
193{
194    // TODO: Conflict handling here
195    already_exists(&dst)?;
196
197    #[cfg(windows)]
198    fn imp(src: &Path, dst: &Path) -> std::io::Result<()> {
199        use std::os::windows::fs::symlink_dir;
200        symlink_dir(src, dst)
201    }
202
203    #[cfg(unix)]
204    fn imp(src: &Path, dst: &Path) -> std::io::Result<()> {
205        use std::os::unix::fs::symlink;
206        symlink(src, dst)
207    }
208
209    imp(src.as_ref(), dst.as_ref()).map_err(|e| {
210        std::io::Error::new(
211            std::io::ErrorKind::Other,
212            format!(
213                "failed to symlink directory {} with target {}: {}",
214                src.as_ref().display(),
215                dst.as_ref().display(),
216                e
217            ),
218        )
219    })
220}
221
222/// Create a file symlink to the given src with the given link name.
223/// taken from https://github.com/Byron/jwalk/blob/0079deb9faed6be48e77676494351f06411db5de/tests/util/mod.rs#L147
224/// Copyright (c) 2019 Jesse Grosjean
225pub fn symlink_file<A, D>(src: A, dst: D) -> std::io::Result<()>
226where
227    A: AsRef<Path>,
228    D: AsRef<Path>,
229{
230    // TODO: Conflict handling here
231    already_exists(&dst)?;
232
233    #[cfg(windows)]
234    fn imp(src: &Path, dst: &Path) -> std::io::Result<()> {
235        use std::os::windows::fs::symlink_file;
236        symlink_file(src, dst)
237    }
238
239    #[cfg(unix)]
240    fn imp(src: &Path, dst: &Path) -> std::io::Result<()> {
241        use std::os::unix::fs::symlink;
242        symlink(src, dst)
243    }
244
245    imp(src.as_ref(), dst.as_ref()).map_err(|e| {
246        std::io::Error::new(
247            std::io::ErrorKind::Other,
248            format!(
249                "failed to symlink file {} with target {}: {}",
250                src.as_ref().display(),
251                dst.as_ref().display(),
252                e
253            ),
254        )
255    })
256}