1use crate::ResultExt;
2use anyhow::{Result, bail};
3use async_fs as fs;
4use futures_lite::StreamExt;
5use std::path::{Path, PathBuf};
6
7/// Removes all files and directories matching the given predicate
8pub async fn remove_matching<F>(dir: &Path, predicate: F)
9where
10 F: Fn(&Path) -> bool,
11{
12 if let Some(mut entries) = fs::read_dir(dir).await.log_err() {
13 while let Some(entry) = entries.next().await {
14 if let Some(entry) = entry.log_err() {
15 let entry_path = entry.path();
16 if predicate(entry_path.as_path())
17 && let Ok(metadata) = fs::metadata(&entry_path).await {
18 if metadata.is_file() {
19 fs::remove_file(&entry_path).await.log_err();
20 } else {
21 fs::remove_dir_all(&entry_path).await.log_err();
22 }
23 }
24 }
25 }
26 }
27}
28
29pub async fn collect_matching<F>(dir: &Path, predicate: F) -> Vec<PathBuf>
30where
31 F: Fn(&Path) -> bool,
32{
33 let mut matching = vec![];
34
35 if let Some(mut entries) = fs::read_dir(dir).await.log_err() {
36 while let Some(entry) = entries.next().await {
37 if let Some(entry) = entry.log_err()
38 && predicate(entry.path().as_path()) {
39 matching.push(entry.path());
40 }
41 }
42 }
43
44 matching
45}
46
47pub async fn find_file_name_in_dir<F>(dir: &Path, predicate: F) -> Option<PathBuf>
48where
49 F: Fn(&str) -> bool,
50{
51 if let Some(mut entries) = fs::read_dir(dir).await.log_err() {
52 while let Some(entry) = entries.next().await {
53 if let Some(entry) = entry.log_err() {
54 let entry_path = entry.path();
55
56 if let Some(file_name) = entry_path
57 .file_name()
58 .map(|file_name| file_name.to_string_lossy())
59 && predicate(&file_name) {
60 return Some(entry_path);
61 }
62 }
63 }
64 }
65
66 None
67}
68
69pub async fn move_folder_files_to_folder<P: AsRef<Path>>(
70 source_path: P,
71 target_path: P,
72) -> Result<()> {
73 if !target_path.as_ref().is_dir() {
74 bail!("Folder not found or is not a directory");
75 }
76
77 let mut entries = fs::read_dir(source_path.as_ref()).await?;
78 while let Some(entry) = entries.next().await {
79 let entry = entry?;
80 let old_path = entry.path();
81 let new_path = target_path.as_ref().join(entry.file_name());
82
83 fs::rename(&old_path, &new_path).await?;
84 }
85
86 fs::remove_dir(source_path).await?;
87
88 Ok(())
89}
90
91#[cfg(unix)]
92/// Set the permissions for the given path so that the file becomes executable.
93/// This is a noop for non-unix platforms.
94pub async fn make_file_executable(path: &Path) -> std::io::Result<()> {
95 fs::set_permissions(
96 path,
97 <fs::Permissions as fs::unix::PermissionsExt>::from_mode(0o755),
98 )
99 .await
100}
101
102#[cfg(not(unix))]
103#[allow(clippy::unused_async)]
104/// Set the permissions for the given path so that the file becomes executable.
105/// This is a noop for non-unix platforms.
106pub async fn make_file_executable(_path: &Path) -> std::io::Result<()> {
107 Ok(())
108}