nat-poc 0.1.7

A proof of concept re-write of natls
#![allow(dead_code)]
mod input;
mod text_effects;
use std::os::unix::fs::{MetadataExt, FileTypeExt};
use structopt::StructOpt;
use termion;

struct Directory {
  paths: Vec<File>,
}

enum DirSortType {
  Name,
  Created,
  Modified,
  Size,
  Not,
}

#[derive(Clone)]
struct File {
  path: std::path::PathBuf,
  file_type: Vec<PathType>,
}

#[derive(Copy, Clone, Debug)]
enum PathType {
  Dir,
  Symlink,
  Path,
  Pipe,
  CharD,
  BlockD,
  Socket,
}

impl PathType {
  fn new(file: &std::path::PathBuf) -> Result<Vec<Self>, Box<dyn std::error::Error>> {
    let mut return_val = Vec::new();
    if file.symlink_metadata()?.is_dir() { return_val.push(Self::Dir) } 
    if file.symlink_metadata()?.file_type().is_symlink() { return_val.push(Self::Symlink) }
    if file.symlink_metadata()?.file_type().is_fifo() { return_val.push(Self::Pipe) } 
    if file.symlink_metadata()?.file_type().is_char_device() { return_val.push(Self::CharD) }
    if file.symlink_metadata()?.file_type().is_block_device() { return_val.push(Self::BlockD) }
    if file.symlink_metadata()?.file_type().is_socket() { return_val.push(Self::Socket) } 
    if return_val.is_empty() { return_val.push(Self::Path) }

    Ok(return_val)
  }

  fn get_color_for_type(&self) -> String {
    match self {
      Self::Dir => format!("{}", termion::color::Fg(termion::color::LightBlue)),
      Self::Symlink => format!("{}", termion::color::Fg(termion::color::LightMagenta)),
      Self::Path => format!("{}", termion::color::Fg(termion::color::White)),
      Self::Pipe => format!("{}", termion::color::Fg(termion::color::Yellow)),
      Self::CharD => format!("{}", termion::color::Fg(termion::color::LightGreen)),
      Self::BlockD => format!("{}", termion::color::Fg(termion::color::LightGreen)),
      Self::Socket => format!("{}", termion::color::Fg(termion::color::LightGreen)),
    }
  }

  fn get_text_traits_for_type(&self, name: &str, file: &std::path::PathBuf) -> String {
    match self {
      Self::Dir => text_effects::bold(&format!("{}{}/", name, termion::color::Fg(termion::color::White))),
      Self::Symlink => text_effects::italic(&format!("{} -> {}", name, std::fs::canonicalize(std::fs::read_link(file).unwrap()).unwrap_or(file.clone()).to_str().unwrap_or(name))),
      Self::Path => text_effects::bold(name),
      Self::Pipe => text_effects::bold(&format!("{}{}|", name, termion::color::Fg(termion::color::White))),
      Self::CharD => text_effects::bold(name),
      Self::BlockD => text_effects::bold(name),
      Self::Socket => text_effects::bold(name),
    }
  }
}

impl File {
  fn new(file: std::path::PathBuf) -> Self {
    Self {
      file_type: PathType::new(&file).unwrap(),
      path: file,
    }
  }
}

fn get_sort_type(sort_t: [bool; 4]) -> DirSortType {
  for (i, t) in sort_t.iter().enumerate() {
    if *t {
      match i {
        0 => return DirSortType::Name,
        1 => return DirSortType::Created,
        2 => return DirSortType::Modified, 
        3 => return DirSortType::Size,
        _ => ()
      }
    }
  }
  DirSortType::Not
}

impl Directory {
  fn new(dir: std::path::PathBuf) -> Result<Self, Box<dyn std::error::Error>> {
    if !std::path::Path::new(&dir).exists() {
      let mut new_paths = Vec::new();
      let paths = std::fs::read_dir(".")?
        .map(|res| res.map(|e| e.path()))
        .collect::<Result<Vec<std::path::PathBuf>, std::io::Error>>()?;

      for p in paths {
        if p
          .file_name()
            .unwrap()
            .to_str()
            .unwrap()
            .to_lowercase()
            .contains(&dir.display().to_string().to_lowercase())
            {
              let f = File::new(p);
              new_paths.push(f)
            }
      }

      if new_paths.is_empty() {
        println!("Path could not be found");
        std::process::exit(1)
      }

      Ok (
        Self {
          paths: new_paths,
        }
      )
    } else {
      let paths = std::fs::read_dir(dir)?
        .map(|res| res.map(|e| File::new(e.path())))
        .collect::<Result<Vec<File>, std::io::Error>>()?;
      Ok(
        Self {
          paths,
        }
      )
    }
  }

  fn self_name_sort(&mut self) {
    self.paths.sort_by(|a, b| a.path.file_name().unwrap().to_str().unwrap().to_lowercase().cmp(&b.path.file_name().unwrap().to_str().unwrap().to_lowercase()))
  }

  fn self_create_sort(&mut self) {
    self.paths.sort_by(|a, b| a.path.symlink_metadata().unwrap().created().unwrap().cmp(&b.path.symlink_metadata().unwrap().created().unwrap()))
  }

  fn self_modified_sort(&mut self) {
    self.paths.sort_by(|a, b| a.path.symlink_metadata().unwrap().modified().unwrap().cmp(&b.path.symlink_metadata().unwrap().modified().unwrap()))
  }

  fn self_size_sort(&mut self) {
    self.paths.sort_by(|a, b| a.path.symlink_metadata().unwrap().size().cmp(&b.path.symlink_metadata().unwrap().size()))
  }

  fn sort_directory_then_path(&mut self) {
    let new = &self.paths;
    let mut newer = Vec::new();
    let mut directories = Vec::new();
    for (i, f) in new.iter().enumerate() {
      if f.path.symlink_metadata().unwrap().is_dir() {
        directories.push(new[i].clone());
      } else {
        newer.push(new[i].clone())
      }
    } 

    match get_sort_type([input::Cli::from_args().name, input::Cli::from_args().created, input::Cli::from_args().modified, input::Cli::from_args().size]) {
      DirSortType::Name => {
        name_sort(&mut directories);
        name_sort(&mut newer)
      },
      DirSortType::Created => {
        create_sort(&mut directories);
        create_sort(&mut newer)
      },
      DirSortType::Modified => {
        modified_sort(&mut directories);
        modified_sort(&mut newer)
      },
      DirSortType::Size => {
        size_sort(&mut directories);
        size_sort(&mut newer)
      },
      DirSortType::Not => (),
    }

    directories.append(&mut newer);
    self.paths = directories; 
  }

  fn sort_paths(&mut self) {
    match get_sort_type([input::Cli::from_args().name, input::Cli::from_args().created, input::Cli::from_args().modified, input::Cli::from_args().size]) {
      DirSortType::Name => self.self_name_sort(),
      DirSortType::Created => self.self_create_sort(),
      DirSortType::Modified => self.self_modified_sort(),
      DirSortType::Size => self.self_size_sort(),
      DirSortType::Not => (),
    }
  }

  fn sort(&mut self) {
    match input::Cli::from_args().gdf {
      true => self.sort_directory_then_path(),
      false => self.sort_paths(),
    }
  }
}

fn name_sort(dir: &mut Vec<File>) {
  dir.sort_by(|a, b| a.path.file_name().unwrap().to_str().unwrap().to_lowercase().cmp(&b.path.file_name().unwrap().to_str().unwrap().to_lowercase()))
}

fn create_sort(dir: &mut Vec<File>) {
  dir.sort_by(|a, b| a.path.symlink_metadata().unwrap().created().unwrap().cmp(&b.path.symlink_metadata().unwrap().created().unwrap()))
}

fn modified_sort(dir: &mut Vec<File>) {
  dir.sort_by(|a, b| a.path.symlink_metadata().unwrap().modified().unwrap().cmp(&b.path.symlink_metadata().unwrap().modified().unwrap()))
}

fn size_sort(dir: &mut Vec<File>) {
  dir.sort_by(|a, b| a.path.symlink_metadata().unwrap().size().cmp(&b.path.symlink_metadata().unwrap().size()))
}

impl std::fmt::Display for File {
  fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
    let mut res = String::new();
    for (i, v) in self.file_type.iter().enumerate() {
      if i == 0 {
        res = v.get_text_traits_for_type(self.path.file_name().unwrap().to_str().unwrap(), &self.path);
        res = format!("{}{}", v.get_color_for_type(), res);
      } else {
        res = v.get_text_traits_for_type(&res, &self.path);
        res = format!("{}{}", v.get_color_for_type(), res);
      }
    }
    write!(f, "{}", res )
  }
}

impl std::fmt::Display for Directory {
  fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
    Ok(
      for i in self.paths.iter() {
        write!(f, "{}\n", i)?;
      }
    )
  }
}

fn main() {
  let mut dir = Directory::new(input::Cli::from_args().dir).unwrap();
  dir.sort();
  println!("{}", dir)
}

#[cfg(test)]
mod tests;