fs.rs

  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}