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