diff --git a/bash_setup.sh b/bash_setup.sh index 318e8c8..aefd5e4 100644 --- a/bash_setup.sh +++ b/bash_setup.sh @@ -4,15 +4,22 @@ export PATH="$PATH:$PWD/target/debug/" pid=( $(ps -o ppid) ) arg_pid=" --pid ${pid[-2]} " +__call_navigate() { + eval "$(navigate ${arg_pid} $@)" +} push() { - \builtin cd -- "$(navigate ${arg_pid} push $@)" + __call_navigate "push $@" } pop() { - \builtin cd -- "$(navigate ${arg_pid} pop $@)" + __call_navigate "pop $@" } stack() { - echo "$(navigate ${arg_pid} stack $@)" + __call_navigate "stack $@" +} + +book() { + __call_navigate "bookmark $@" } diff --git a/src/arguments.rs b/src/arguments.rs index 6ff282a..7224d87 100644 --- a/src/arguments.rs +++ b/src/arguments.rs @@ -38,7 +38,7 @@ pub struct PushArgs { pub show_stack: Option, /// change to - pub path: PathBuf, + pub path: Option, } #[derive(Debug, Clone, Args)] @@ -70,17 +70,41 @@ pub struct StackArgs { #[allow(non_camel_case_types)] pub enum StackAction { /// clear stack - clear(ClearArgs), + clear(EmptyArgs), } -#[derive(Debug, Clone, Args)] -pub struct ClearArgs {} - #[derive(Debug, Clone, Args)] pub struct BookmarkArgs { - /// show stack - #[arg(short, long)] - pub show_stack: Option, + /// bookmark subcommand + #[command(subcommand)] + pub bookmark_action: Option, - pub mark: String, + /// name of bookmark to push + pub name: Option, } + +#[derive(Debug, Clone, Subcommand)] +#[allow(non_camel_case_types)] +pub enum BookmarkAction { + /// list all bookmarks + list(EmptyArgs), + + /// add a bookmark with `book add ` + add(BookmarkSubArgs), + + /// remove a bookmark by name `book remove ` + remove(BookmarkSubArgs) +} + +#[derive(Debug, Clone, Args)] +pub struct BookmarkSubArgs { + /// name of bookmark to add/remove + pub name: String, + + /// path of bookmark to add + pub path: Option, +} + +/// empty struct for subcommands with no arguments +#[derive(Debug, Clone, Args)] +pub struct EmptyArgs {} diff --git a/src/config.rs b/src/config.rs index e2163c7..ce241f7 100644 --- a/src/config.rs +++ b/src/config.rs @@ -3,48 +3,64 @@ use std::fs; use std::fs::File; -use std::io::{Error, ErrorKind, Result}; -use std::path::{Path, PathBuf}; +use std::io::{Error, Result}; +use std::path::{PathBuf}; use std::str::FromStr; -use sysinfo::{Pid, System}; - -const config_dir_path: &str = "~/.config/navigate/"; +use std::collections::HashMap; +use std::env::var; #[derive(Debug, Clone)] pub struct Config { - pub bookmarks: Vec, + pub conf_dir: PathBuf, + pub bookmarks: HashMap, } impl Config { /// generates and populates a new instance of Config pub fn new() -> Result { - let mut config = Config { - bookmarks: Vec::::new(), + let mut bookmarks = Config { + conf_dir: PathBuf::new(), + bookmarks: HashMap::::new(), }; - - // stack.build_stack()?; - if !config.bookmarks[0].is_dir() { - config.bookmarks.remove(0); + let home_dir = match var("HOME") { + Ok(value) => value, + Err(error) => return Err(Error::other(error.to_string())), }; + bookmarks.conf_dir = match PathBuf::from_str(&home_dir) { + Ok(value) => value, + Err(error) => return Err(Error::other(error.to_string())), + }; + bookmarks.conf_dir.push(".config/navigate/"); + bookmarks.build_config()?; - Ok(config) + Ok(bookmarks) } - pub fn build_config(&mut self) -> Result<()> { - let config_dir = match PathBuf::from_str(config_dir_path) { - Ok(result) => result, - Err(_) => return Err(Error::other("failed to create path object for config file")), - }; - - let mut bookmark_file = config_dir.clone(); + fn build_config(&mut self) -> Result<()> { + let mut bookmark_file = self.conf_dir.clone(); bookmark_file.push("bookmarks.conf"); if !bookmark_file.is_file() { _ = File::create(bookmark_file.clone())?; } - let mut bookmarks = fs::read_to_string(bookmark_file.clone())?; - // TODO: parse bookmarks + let bookmarks = fs::read_to_string(bookmark_file)?; + let bookmarks = bookmarks.split("\n"); + for entry in bookmarks { + let tokens: Vec<&str> = entry.split("=").collect(); + if tokens.len() != 2 { + continue; + } + let key: String = String::from(tokens[0]); + let path = match PathBuf::from_str(tokens[1]) { + Ok(value) => value, + Err(err) => return Err(Error::other(err.to_string())), + }; + if !path.is_dir() { + continue; + } + self.bookmarks.insert(key, path); + } Ok(()) } diff --git a/src/main.rs b/src/main.rs index 38030a1..2b46d15 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,46 +3,74 @@ mod config; mod stack; use arguments::*; -use clap::{Parser}; +use clap::Parser; +use config::Config; use stack::Stack; -use std::env::current_dir; +use std::env::{current_dir, var}; use std::io::{Error, Result}; +use std::path::{Path, PathBuf}; +use std::str::FromStr; fn main() -> Result<()> { - let args = Arguments::parse(); + let args = match Arguments::try_parse() { + Ok(a) => a, + Err(e) => { + print!("echo '{}'", e); + return Ok(()); + } + }; let mut stack = match Stack::new(args.pid) { Ok(stack) => stack, - Err(_) => return Err(Error::other("-- failed to build stack")), + Err(_) => { + print!("echo "); + return Err(Error::other("-- failed to build stack")); + } }; - - match args.action { + let res = match args.action { Action::push(push_args) => handle_push(&push_args, &mut stack), Action::pop(pop_args) => handle_pop(&pop_args, &mut stack), Action::stack(stack_args) => handle_stack(&stack_args, &mut stack), Action::bookmark(bookmark_args) => handle_bookmark(&bookmark_args, &mut stack), - } -} + }; -pub fn handle_push(args: &PushArgs, stack: &mut Stack) -> Result<()> { - // TODO: handle arguments - if !args.path.is_dir() { - return Err(Error::other("-- invalid path argument")); + if res.is_err() { + print!("echo '{}'", res.unwrap_err()); } - let current_path = current_dir()?; - stack.push_entry(¤t_path)?; - println!("{}", args.path.to_str().unwrap()); Ok(()) } -pub fn handle_pop(args: &PopArgs, stack: &mut Stack) -> Result<()> { +fn handle_push(args: &PushArgs, stack: &mut Stack) -> Result<()> { + let path = match args.path.clone() { + Some(value) => value, + None => { + let home_dir = match var("HOME") { + Ok(value) => value, + Err(error) => return Err(Error::other(error.to_string())), + }; + match PathBuf::from_str(&home_dir) { + Ok(value) => value, + Err(error) => return Err(Error::other(error.to_string())), + } + } + }; + push_path(&path, stack)?; + Ok(()) +} + +fn handle_pop(_args: &PopArgs, stack: &mut Stack) -> Result<()> { // TODO: handle arguments let path = stack.pop_entry()?; - println!("{}", path.to_str().unwrap()); + println!( + "cd -- {}", + match path.to_str() { + Some(value) => value, + None => return Err(Error::other("-- failed to print popped path as string")), + } + ); Ok(()) } -pub fn handle_stack(args: &StackArgs, stack: &mut Stack) -> Result<()> { - // TODO: handle arguments +fn handle_stack(args: &StackArgs, stack: &mut Stack) -> Result<()> { if args.stack_action.is_some() { match args.stack_action.clone().unwrap() { StackAction::clear(_) => return stack.clear_stack(), @@ -55,12 +83,69 @@ pub fn handle_stack(args: &StackArgs, stack: &mut Stack) -> Result<()> { } // print stack to standard output for (n, item) in output.iter().rev().enumerate() { - println!("{} {}", n, item.to_str().unwrap()); + println!("echo '{} - {}'", n, item.to_str().unwrap()); } Ok(()) } -pub fn handle_bookmark(args: &BookmarkArgs, stack: &mut Stack) -> Result<()> { - // TODO: handle arguments +fn handle_bookmark(args: &BookmarkArgs, stack: &mut Stack) -> Result<()> { + let mut config = match Config::new() { + Ok(value) => value, + Err(error) => return Err(Error::other(error.to_string())), + }; + // if args.bookmark_action.is_some() { + if args.bookmark_action.is_some() { + match args.bookmark_action.clone().unwrap() { + BookmarkAction::list(_) => list_bookmarks(&config)?, + BookmarkAction::add(args) => add_bookmarks(&args, &mut config)?, + BookmarkAction::remove(args) => remove_bookmarks(&args, &mut config)?, + }; + } else if args.name.is_some() { + let path = match config + .bookmarks + .get(args.name.as_ref().unwrap()) + { + Some(value) => value, + None => return Err(Error::other("requested bookmark does not exist")), + }; + push_path(path, stack)?; + } else { + return Err(Error::other("-- provide either a `subcommand` or a `bookmark name`")); + } + Ok(()) +} + +fn list_bookmarks(config: &Config) -> Result<()> { + let mut buffer = String::new(); + for (mark, path) in &config.bookmarks { + buffer.push_str(&format!("{} : {}\n", mark, path.to_str().unwrap())); + } + println!("echo '{}'", buffer); + Ok(()) +} + +fn add_bookmarks(_args: &BookmarkSubArgs, _bookmarks: &mut Config) -> Result<()> { + Ok(()) +} + +fn remove_bookmarks(_args: &BookmarkSubArgs, _bookmarks: &mut Config) -> Result<()> { + Ok(()) +} + +/// push path to stack and print command to navigate to provided path +fn push_path(path: &Path, stack: &mut Stack) -> Result<()> { + if !path.is_dir() { + return Err(Error::other("-- invalid path argument")); + } + let current_path = current_dir()?; + let next_path = path.canonicalize()?; + stack.push_entry(¤t_path)?; + println!( + "cd -- {}", + match next_path.to_str() { + Some(value) => value, + None => return Err(Error::other("-- failed to print provided path as string")), + } + ); Ok(()) } diff --git a/src/stack.rs b/src/stack.rs index 0f6fadf..b2cccb4 100644 --- a/src/stack.rs +++ b/src/stack.rs @@ -1,6 +1,6 @@ use std::fs; use std::fs::File; -use std::io::{Error, ErrorKind, Result}; +use std::io::{Error, Result}; use std::path::{Path, PathBuf}; use std::str::FromStr; use sysinfo::{Pid, System}; @@ -58,9 +58,8 @@ impl Stack { match entry { Some(entry) => Ok(entry), - None => Err(Error::new( - ErrorKind::Other, - "pop failed to retrieve item from stack", + None => Err(Error::other( + "-- pop failed to retrieve item from stack, it might be empty", )), } } @@ -73,12 +72,11 @@ impl Stack { self.stack .len() .checked_sub(entry_number) - .expect("requested entry number is out of bounds"), + .expect("-- requested entry number is out of bounds"), ) { Some(item) => Ok(item), - None => Err(Error::new( - ErrorKind::Other, - "failed to retrieve stack entry by number", + None => Err(Error::other( + "-- failed to retrieve stack entry by number", )), } } @@ -86,7 +84,7 @@ impl Stack { /// clean up dead stack files, parse and build stack fn build_stack(&mut self) -> Result<()> { let stack_dir: PathBuf = PathBuf::from_str("/tmp/navigation/") - .expect("failed to create path object of '/tmp/navigation'"); + .expect("-- failed to create path object of '/tmp/navigation'"); let mut sys = System::new_all(); sys.refresh_all(); let procs = sys.processes(); @@ -100,10 +98,10 @@ impl Stack { entry .file_name() .to_str() - .expect("failed to convert file name to str"), + .expect("-- failed to convert file name to str"), ); - if !procs.contains_key(&process_id.expect("failed to convert filename to pid")) { - fs::remove_file(entry.path()).expect("failed to remove orphaned file"); + if !procs.contains_key(&process_id.expect("-- failed to convert filename to pid")) { + fs::remove_file(entry.path()).expect("-- failed to remove orphaned file"); } } } else { @@ -142,7 +140,7 @@ impl Stack { output.push( entry .to_str() - .expect("failed to convert stack entry to string"), + .expect("-- failed to convert stack entry to string"), ); } fs::write(self.path.clone(), output.join("\n"))?;