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                {
 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)]
 95/// Set the permissions for the given path so that the file becomes executable.
 96/// This is a noop for non-unix platforms.
 97pub 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)]
107/// Set the permissions for the given path so that the file becomes executable.
108/// This is a noop for non-unix platforms.
109pub async fn make_file_executable(_path: &Path) -> std::io::Result<()> {
110    Ok(())
111}