diff --git a/src/directory.rs b/src/directory.rs index 7cb9d51..49c060b 100644 --- a/src/directory.rs +++ b/src/directory.rs @@ -1,6 +1,10 @@ mod display; mod item; +use std::fs::{remove_dir_all, remove_file, DirBuilder, File}; +use std::io::Error; +use std::path::PathBuf; + use tui::widgets::{Block, Borders, List, ListItem}; use tui::style::{Color, Style}; use tui::layout::Rect; @@ -17,12 +21,37 @@ pub struct Directory { editing: bool, } impl Directory { - pub fn new(root: String) -> Self { - Self { + pub fn new(root: String) -> Result { + DirBuilder::new() + .recursive(true) + .create(&root)?; + + Ok(Self { dirs: vec![Item::new(root, 0, false)], selection: 0, editing: false, - } + }) + } + + pub fn path(&self) -> PathBuf { + let s = self.selected(); + let init = (PathBuf::from(s.name.as_str()), s.depth); + let (result, _) = self.dirs[0..=self.selection()] + .iter() + .rev() + .fold( + init, + |(mut path, mut depth), d| { + if d.depth < depth { + path = PathBuf::from(&d.name).join(path); + depth = d.depth; + } + + (path, depth) + } + ); + + result } pub fn select_prev(&mut self) { @@ -47,9 +76,30 @@ impl Directory { }; let new = Item::new(name, depth, is_file); - self.dirs.insert(self.selection()+1, new); + + self.selection += 1; + self.dirs.insert(self.selection(), new); + } + + fn create(&self) -> Result<(), Error> { + if self.selected().is_file { + File::create(self.path())?; + } else { + DirBuilder::new() + .recursive(true) + .create(self.path())?; + } + Ok(()) } + fn delete(&self) -> Result<(), Error> { + if self.selected().is_file { + remove_file(self.path()) + } else { + remove_dir_all(self.path()) + } + } + fn selected(&self) -> &Item { &self.dirs[self.selection()] } @@ -69,12 +119,10 @@ impl Directory { match c { 'f' | 'F' => { self.insert(String::new(), true); - self.select_next(); self.editing = true; }, 'd' | 'D' => { self.insert(String::new(), false); - self.select_next(); self.editing = true; }, 'x' | 'X' => { @@ -89,12 +137,22 @@ impl Directory { self.editing = true; }, KeyCode::Esc => return Message::Exit, - KeyCode::Backspace => { + KeyCode::Backspace if self.selected().depth > 0 => { + self.delete().unwrap(); if self.selected().is_file { self.dirs.remove(self.selection()); - if self.selection() == self.dirs.len() { - self.selection -= 1; - } + } else { + let target_depth = self.selected().depth; + + let end = (self.selection()+1 .. self.dirs.len()) + .find(|&i| self.dirs[i].depth <= target_depth) + .unwrap_or(self.dirs.len()); + + self.dirs.drain(self.selection()..end); + + } + if self.selection() >= self.dirs.len() { + self.selection -= 1; } } _ => (), @@ -107,9 +165,12 @@ impl Directory { match key { KeyCode::Char(c) => self.selected_mut().name.push(c), KeyCode::Enter | KeyCode::Esc if !self.selected().name.is_empty() => { + self.create().unwrap(); self.editing = false; }, - KeyCode::Backspace => { self.selected_mut().name.pop(); }, + KeyCode::Backspace => { + self.selected_mut().name.pop(); + }, _ => (), } diff --git a/src/main.rs b/src/main.rs index b217f9d..4e3040f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,7 +17,7 @@ fn main() -> Result<(), Error> { let mut terminal = Terminal::new()?; let area = terminal.size()?; - let mut state = State::new(area); + let mut state = State::new(area)?; loop { state.render(&mut terminal)?; diff --git a/src/state.rs b/src/state.rs index ae5504c..13efd20 100644 --- a/src/state.rs +++ b/src/state.rs @@ -22,13 +22,13 @@ pub struct State { command: Option> } impl State { - pub fn new(area: Rect) -> Self { - let dir = Directory::new("root".into()); - Self { + pub fn new(area: Rect) -> Result { + let dir = Directory::new("root".into())?; + Ok(Self { dir, size: area, command: None, - } + }) } pub fn render(&self, terminal: &mut Terminal) -> Result<(), Error> { @@ -112,11 +112,13 @@ impl State { else { unsafe { unreachable_unchecked() } }; let mut iter = cmd.buf() - .split(' '); + .split(' ') + .filter(|s| !s.is_empty()); let prog_name = iter.next().unwrap(); let output = Executable::new(prog_name) .args(iter) + .arg(self.dir.path()) .output(); let command_output = CommandOutput::new(output.map(|o| o.stdout));