fs.rs

  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}