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