LCOV - differential code coverage report
Current view: top level - libs/utils/src - fs_ext.rs (source / functions) Coverage Total Hit LBC UBC CBC
Current: cd44433dd675caa99df17a61b18949c8387e2242.info Lines: 96.6 % 89 86 1 2 86
Current Date: 2024-01-09 02:06:09 Functions: 80.0 % 30 24 6 24
Baseline: 66c52a629a0f4a503e193045e0df4c77139e344b.info
Baseline Date: 2024-01-08 15:34:46

           TLA  Line data    Source code
       1                 : /// Extensions to `std::fs` types.
       2                 : use std::{fs, io, path::Path};
       3                 : 
       4                 : use anyhow::Context;
       5                 : 
       6                 : pub trait PathExt {
       7                 :     /// Returns an error if `self` is not a directory.
       8                 :     fn is_empty_dir(&self) -> io::Result<bool>;
       9                 : }
      10                 : 
      11                 : impl<P> PathExt for P
      12                 : where
      13                 :     P: AsRef<Path>,
      14                 : {
      15 CBC         960 :     fn is_empty_dir(&self) -> io::Result<bool> {
      16             960 :         Ok(fs::read_dir(self)?.next().is_none())
      17             960 :     }
      18                 : }
      19                 : 
      20             289 : pub async fn is_directory_empty(path: impl AsRef<Path>) -> anyhow::Result<bool> {
      21             289 :     let mut dir = tokio::fs::read_dir(&path)
      22             283 :         .await
      23             289 :         .context(format!("read_dir({})", path.as_ref().display()))?;
      24             287 :     Ok(dir.next_entry().await?.is_none())
      25             289 : }
      26                 : 
      27               3 : pub async fn list_dir(path: impl AsRef<Path>) -> anyhow::Result<Vec<String>> {
      28               3 :     let mut dir = tokio::fs::read_dir(&path)
      29               3 :         .await
      30               3 :         .context(format!("read_dir({})", path.as_ref().display()))?;
      31                 : 
      32               3 :     let mut content = vec![];
      33               6 :     while let Some(next) = dir.next_entry().await? {
      34               3 :         let file_name = next.file_name();
      35               3 :         content.push(file_name.to_string_lossy().to_string());
      36               3 :     }
      37                 : 
      38               3 :     Ok(content)
      39               3 : }
      40                 : 
      41            7655 : pub fn ignore_not_found(e: io::Error) -> io::Result<()> {
      42            7655 :     if e.kind() == io::ErrorKind::NotFound {
      43            7655 :         Ok(())
      44                 :     } else {
      45 LBC         (1) :         Err(e)
      46                 :     }
      47 CBC        7655 : }
      48                 : 
      49             944 : pub fn ignore_absent_files<F>(fs_operation: F) -> io::Result<()>
      50             944 : where
      51             944 :     F: Fn() -> io::Result<()>,
      52             944 : {
      53             944 :     fs_operation().or_else(ignore_not_found)
      54             944 : }
      55                 : 
      56                 : #[cfg(test)]
      57                 : mod test {
      58                 :     use crate::fs_ext::{is_directory_empty, list_dir};
      59                 : 
      60                 :     use super::ignore_absent_files;
      61                 : 
      62               1 :     #[test]
      63               1 :     fn is_empty_dir() {
      64               1 :         use super::PathExt;
      65               1 : 
      66               1 :         let dir = camino_tempfile::tempdir().unwrap();
      67               1 :         let dir_path = dir.path();
      68                 : 
      69                 :         // test positive case
      70               1 :         assert!(
      71               1 :             dir_path.is_empty_dir().expect("test failure"),
      72 UBC           0 :             "new tempdir should be empty"
      73                 :         );
      74                 : 
      75                 :         // invoke on a file to ensure it returns an error
      76 CBC           1 :         let file_path = dir_path.join("testfile");
      77               1 :         let f = std::fs::File::create(&file_path).unwrap();
      78               1 :         drop(f);
      79               1 :         assert!(file_path.is_empty_dir().is_err());
      80                 : 
      81                 :         // do it again on a path, we know to be nonexistent
      82               1 :         std::fs::remove_file(&file_path).unwrap();
      83               1 :         assert!(file_path.is_empty_dir().is_err());
      84               1 :     }
      85                 : 
      86               1 :     #[tokio::test]
      87               1 :     async fn is_empty_dir_async() {
      88               1 :         let dir = camino_tempfile::tempdir().unwrap();
      89               1 :         let dir_path = dir.path();
      90                 : 
      91                 :         // test positive case
      92               1 :         assert!(
      93               1 :             is_directory_empty(dir_path).await.expect("test failure"),
      94 UBC           0 :             "new tempdir should be empty"
      95                 :         );
      96                 : 
      97                 :         // invoke on a file to ensure it returns an error
      98 CBC           1 :         let file_path = dir_path.join("testfile");
      99               1 :         let f = std::fs::File::create(&file_path).unwrap();
     100               1 :         drop(f);
     101               1 :         assert!(is_directory_empty(&file_path).await.is_err());
     102                 : 
     103                 :         // do it again on a path, we know to be nonexistent
     104               1 :         std::fs::remove_file(&file_path).unwrap();
     105               1 :         assert!(is_directory_empty(file_path).await.is_err());
     106                 :     }
     107                 : 
     108               1 :     #[test]
     109               1 :     fn ignore_absent_files_works() {
     110               1 :         let dir = camino_tempfile::tempdir().unwrap();
     111               1 : 
     112               1 :         let file_path = dir.path().join("testfile");
     113               1 : 
     114               1 :         ignore_absent_files(|| std::fs::remove_file(&file_path)).expect("should execute normally");
     115               1 : 
     116               1 :         let f = std::fs::File::create(&file_path).unwrap();
     117               1 :         drop(f);
     118               1 : 
     119               1 :         ignore_absent_files(|| std::fs::remove_file(&file_path)).expect("should execute normally");
     120                 : 
     121               1 :         assert!(!file_path.exists());
     122               1 :     }
     123                 : 
     124               1 :     #[tokio::test]
     125               1 :     async fn list_dir_works() {
     126               1 :         let dir = camino_tempfile::tempdir().unwrap();
     127               1 :         let dir_path = dir.path();
     128                 : 
     129               1 :         assert!(list_dir(dir_path).await.unwrap().is_empty());
     130                 : 
     131               1 :         let file_path = dir_path.join("testfile");
     132               1 :         let _ = std::fs::File::create(&file_path).unwrap();
     133                 : 
     134               1 :         assert_eq!(&list_dir(dir_path).await.unwrap(), &["testfile"]);
     135                 : 
     136               1 :         let another_dir_path = dir_path.join("testdir");
     137               1 :         std::fs::create_dir(another_dir_path).unwrap();
     138               1 : 
     139               1 :         let expected = &["testdir", "testfile"];
     140               1 :         let mut actual = list_dir(dir_path).await.unwrap();
     141               1 :         actual.sort();
     142               1 :         assert_eq!(actual, expected);
     143                 :     }
     144                 : }
        

Generated by: LCOV version 2.1-beta