libjess 0.1.1

Utility library for daily tasks
Documentation
use crate::errors::{Result, WrappedError};
use std::fs::{self, DirEntry};
use std::path::{Path, PathBuf};

/// List entries in the specified directory by iterating on the [`std::fs::read_dir`] iterator.
///
/// Calls cb when then iterator yields a DirEntry, otherwise calls cb_err with the error
///
/// # Errors
///
/// See error section on [`std::fs::read_dir`]
///
/// # Examples
///
/// Iterate over entries in a directory, printing their path to stdout
///
/// ```rust
/// use libjess::prelude::v1::*;
/// let _ = ls_dir(".",
///     &mut |dir_entry| println!("{:?}", dir_entry.path()),
///     &mut |err| eprintln!("{:?}", err),
/// );
/// ```
pub fn ls_dir<C, E>(path: impl AsRef<Path>, cb: &mut C, cb_err: &mut E) -> Result<()>
where
    C: FnMut(&DirEntry),
    E: FnMut(&WrappedError),
{
    let dir_iter = fs::read_dir(&path)
        .map_err(|base_error| WrappedError::io_error(base_error, PathBuf::from(path.as_ref())))?;
    for dir_entry in dir_iter {
        match dir_entry {
            Ok(dir_entry) => {
                cb(&dir_entry);
            }
            Err(err) => cb_err(&WrappedError::io_error(err, PathBuf::from(path.as_ref()))),
        }
    }
    Ok(())
}

/// Works similarly to [`self::ls_dir`] except recursively.
///
/// # Errors
///
/// See error section on [`std::fs::read_dir`]
///
/// # Examples
///
/// Iterate over entries in a directory, printing their path to stdout
///
/// ```rust
/// use libjess::prelude::v1::*;
/// let _ = walk(".",
///     &mut |dir_entry| println!("{:?}", dir_entry.path()),
///     &mut |err| eprintln!("{:?}", err),
/// );
/// ```

pub fn walk<C, E>(path: impl AsRef<Path>, cb: &mut C, cb_err: &mut E) -> Result<()>
where
    C: FnMut(&DirEntry),
    E: FnMut(&WrappedError),
{
    let mut iter_list = vec![];

    let root = fs::read_dir(path.as_ref())
        .map_err(|err| WrappedError::io_error(err, PathBuf::from(path.as_ref())))?;
    iter_list.push(root.collect::<Vec<_>>().into_iter());
    while let Some(current) = iter_list.pop() {
        for dir_entry in current {
            match dir_entry {
                Ok(dir_entry) => {
                    if let Ok(ft) = dir_entry.file_type() {
                        if ft.is_dir() && !ft.is_symlink() {
                            if let Ok(read_dir) = fs::read_dir(dir_entry.path()) {
                                iter_list.push(read_dir.collect::<Vec<_>>().into_iter());
                            }
                        }
                    }
                    cb(&dir_entry)
                }
                Err(base_error) => cb_err(&WrappedError::io_error(
                    base_error,
                    PathBuf::from(path.as_ref()),
                )),
            }
        }
    }
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::ffi::OsString;
    use std::io;

    #[test]
    fn walk_directory() {
        let mut all_files = vec![];
        let result = walk(
            "..",
            &mut |entry| all_files.push(entry.file_name()),
            &mut |_| {},
        );

        assert!(result.is_ok());

        assert!(all_files.contains(&OsString::from("lib.rs")));
        assert!(all_files.contains(&OsString::from("dir.rs")));
        assert!(all_files.contains(&OsString::from("fs.rs")));
    }
    #[test]
    fn walk_file() {
        let result = walk("Cargo.toml", &mut |_| {}, &mut |_| {});
        assert!(result.is_err());

        let _ = result.map_err(|err| {
            let inner = err.get_io_error();
            assert!(inner.is_some());
            let base_error = inner.unwrap();
            assert_eq!(base_error.kind(), io::ErrorKind::Other);
            assert_eq!(20, base_error.raw_os_error().unwrap());
            err
        });
    }

    #[test]
    fn walk_404() {
        let result = walk("404", &mut |_| {}, &mut |_| {});
        assert!(result.is_err());

        let _ = result.map_err(|err| {
            let inner = err.get_io_error();
            assert!(inner.is_some());
            let base_error = inner.unwrap();
            assert_eq!(base_error.kind(), io::ErrorKind::NotFound);
            assert_eq!(2, base_error.raw_os_error().unwrap());
            err
        });
    }
    #[test]
    fn ls_directory() {
        let mut all_files = vec![];
        let result = ls_dir(
            "src",
            &mut |entry| all_files.push(entry.file_name()),
            &mut |_| {},
        );

        assert!(result.is_ok());

        assert!(all_files.contains(&OsString::from("lib.rs")));
        assert!(all_files.contains(&OsString::from("fs.rs")));
        assert!(all_files.contains(&OsString::from("fs")));
    }
    #[test]
    fn ls_file() {
        let result = ls_dir("Cargo.toml", &mut |_| {}, &mut |_| {});
        assert!(result.is_err());
        let _ = result.map_err(|err| {
            let e: std::result::Result<Box<io::Error>, _> =
                err.into_inner().unwrap().downcast::<io::Error>();
            assert!(e.is_ok());
            let base_error = e.unwrap();
            // let inner = err.get_io_error();
            // assert!(inner.is_some());
            // let base_error = inner.unwrap();
            assert_eq!(base_error.kind(), io::ErrorKind::Other);
            assert_eq!(20, base_error.raw_os_error().unwrap());
            // err
        });
    }

    #[test]
    fn ls_404() {
        let result = ls_dir("404", &mut |_| {}, &mut |_| {});
        assert!(result.is_err());

        let _ = result.map_err(|err| {
            let inner = err.get_io_error();
            assert!(inner.is_some());
            let base_error = inner.unwrap();
            assert_eq!(base_error.kind(), io::ErrorKind::NotFound);
            assert_eq!(2, base_error.raw_os_error().unwrap());
            err
        });
    }
}