lla 0.1.2

A lightweight ls replacement
use std::fs::{self, Metadata};
use std::os::unix::fs::PermissionsExt;
use std::os::unix::prelude::MetadataExt;
use std::path::PathBuf;
use std::vec::Vec;

pub trait FileLister {
    fn list_files(
        &self,
        directory: &str,
        recursive: Option<bool>,
        depth: Option<u32>,
    ) -> Vec<PathBuf>;
}

pub struct BasicLister;

impl BasicLister {
    pub fn new() -> BasicLister {
        BasicLister {}
    }
}

impl FileLister for BasicLister {
    fn list_files(
        &self,
        directory: &str,
        _recursive: Option<bool>,
        _depth: Option<u32>,
    ) -> Vec<PathBuf> {
        let entries = fs::read_dir(directory).unwrap();
        let mut files = Vec::new();

        for entry in entries {
            if let Ok(entry) = entry {
                let path = entry.path();
                files.push(path);
            }
        }

        files
    }
}

pub struct LongLister;

impl LongLister {
    pub fn new() -> LongLister {
        LongLister {}
    }

    pub fn get_file_permissions(&self, metadata: &Metadata) -> String {
        let mode = metadata.permissions().mode();
        let mut permissions = String::new();

        let modes_and_permissions = [
            (0o100000, "-"),
            (0o120000, "l"),
            (0o040000, "d"),
            (0o100000, "p"),
            (0o060000, "b"),
            (0o060000, "c"),
            (0o140000, "s"),
        ];
        for &(m, p) in modes_and_permissions.iter() {
            if mode & m == m {
                permissions.push_str(p);
            } else {
                permissions.push_str("-");
            }
        }
        permissions.push_str(&self.get_file_mode(mode));
        permissions
    }

    pub fn get_file_mode(&self, mode: u32) -> String {
        let mut file_mode = String::new();

        let modes_and_permissions = [
            (0o400, "r"),
            (0o200, "w"),
            (0o100, "x"),
            (0o040, "r"),
            (0o020, "w"),
            (0o010, "x"),
            (0o004, "r"),
            (0o002, "w"),
            (0o001, "x"),
        ];
        for &(m, p) in modes_and_permissions.iter() {
            if mode & m == m {
                file_mode.push_str(p);
            } else {
                file_mode.push_str("-");
            }
        }
        file_mode
    }

    pub fn get_user_name(&self, metadata: &Metadata) -> String {
        let uid = metadata.uid();
        let user = users::get_user_by_uid(uid).unwrap();
        user.name().to_string_lossy().to_string()
    }

    pub fn get_group_name(&self, metadata: &Metadata) -> String {
        let gid = metadata.gid();
        let group = users::get_group_by_gid(gid).unwrap();
        group.name().to_string_lossy().to_string()
    }

    pub fn format_size(&self, size: u64) -> String {
        let sizes = ["B", "KB", "MB", "GB", "TB"];
        let mut size = size as f64;
        let mut idx = 0;

        while size >= 1024.0 && idx < sizes.len() - 1 {
            size /= 1024.0;
            idx += 1;
        }

        format!("{:.1} {}", size, sizes[idx])
    }
}

impl FileLister for LongLister {
    fn list_files(
        &self,
        directory: &str,
        _recursive: Option<bool>,
        _depth: Option<u32>,
    ) -> Vec<PathBuf> {
        let entries = fs::read_dir(directory).unwrap();
        let mut files = Vec::new();

        for entry in entries {
            if let Ok(entry) = entry {
                let path = entry.path();
                files.push(path);
            }
        }

        files
    }
}

pub struct TreeLister;

impl TreeLister {
    pub fn new() -> TreeLister {
        TreeLister {}
    }
}

impl FileLister for TreeLister {
    fn list_files(
        &self,
        directory: &str,
        recursive: Option<bool>,
        depth: Option<u32>,
    ) -> Vec<PathBuf> {
        if (depth.is_some() && depth.unwrap() == 0) || (recursive.is_some() && !recursive.unwrap())
        {
            return Vec::new();
        } else {
            let entries = fs::read_dir(directory).unwrap();
            let mut files = Vec::new();

            for entry in entries {
                if let Ok(entry) = entry {
                    let path = entry.path();
                    if path.is_dir() {
                        let mut new_depth = depth.unwrap_or(0);
                        if depth.is_some() {
                            new_depth -= 1;
                        }
                        let mut new_recursive = recursive.unwrap_or(false);
                        if recursive.is_some() {
                            new_recursive = recursive.unwrap();
                        }
                        let mut new_directory = directory.to_string();
                        new_directory.push_str("/");
                        new_directory.push_str(path.file_name().unwrap().to_str().unwrap());
                        files.push(path);
                        files.append(&mut self.list_files(
                            &new_directory,
                            Some(new_recursive),
                            Some(new_depth),
                        ));
                    } else {
                        files.push(path);
                    }
                }
            }

            files
        }
    }
}