From ec4143d0400e0f25729810302def05d2a55d2f2b Mon Sep 17 00:00:00 2001 From: quak Date: Thu, 5 Dec 2024 21:10:23 +0100 Subject: [PATCH] quicksave --- src/config.rs | 125 +++++++++++++++++------ src/format.rs | 268 ++++++++++++++++++++++++++------------------------ src/main.rs | 58 +++++------ src/stack.rs | 8 +- 4 files changed, 270 insertions(+), 189 deletions(-) diff --git a/src/config.rs b/src/config.rs index 82e7126..fe298ce 100644 --- a/src/config.rs +++ b/src/config.rs @@ -9,69 +9,98 @@ use std::io::{Error, Result}; use std::path::PathBuf; use std::str::FromStr; +use crate::{RESET_SEQ, STYLES}; + #[derive(Debug, Clone)] pub struct Config { conf_dir: PathBuf, - settings: Settings, + pub settings: Settings, bookmarks: HashMap, } +#[allow(dead_code)] #[derive(Debug, Clone)] -pub struct Settings {} +pub struct Settings { + pub general: GeneralSettings, + pub styles: StyleSettings, +} + +#[allow(dead_code)] +#[derive(Debug, Clone)] +pub struct GeneralSettings { + pub show_stack_on_push: bool, + pub show_stack_on_pop: bool, + pub show_stack_on_bookmark: bool, +} + +#[allow(dead_code)] +#[derive(Debug, Clone)] +pub struct StyleSettings { + pub note: String, + pub warning: String, + pub error: String, + pub stack_number: String, + pub stack_seperator: String, + pub stack_path: String, + pub bookmarks_key: String, + pub bookmarks_seperator: String, + pub bookmarks_path: String, + pub reset: String, +} impl Config { + const CONFIG_FILE_NAME: &str = "navigate.conf"; const BOOKMARK_FILE_NAME: &str = "bookmarks.conf"; /// generates and populates a new instance of Config pub fn new() -> Result { let mut bookmarks = Config { conf_dir: PathBuf::new(), - settings: Settings{}, + settings: Settings { + general: GeneralSettings { + show_stack_on_push: false, + show_stack_on_pop: false, + show_stack_on_bookmark: false, + }, + styles: StyleSettings { + note: "".to_owned(), + warning: "".to_owned(), + error: "".to_owned(), + stack_number: "".to_owned(), + stack_seperator: "".to_owned(), + stack_path: "".to_owned(), + bookmarks_key: "".to_owned(), + bookmarks_seperator: "".to_owned(), + bookmarks_path: "".to_owned(), + reset: RESET_SEQ.to_owned(), + }, + }, bookmarks: HashMap::::new(), }; + // get home directory path let home_dir = match var("HOME") { Ok(value) => value, Err(error) => return Err(Error::other(error.to_string())), }; + // create PathBuf object from home dir path bookmarks.conf_dir = match PathBuf::from_str(&home_dir) { Ok(value) => value, Err(error) => return Err(Error::other(error.to_string())), }; + // expand home directory path to get configuration directory path bookmarks.conf_dir.push(".config/navigate/"); - bookmarks.build_config()?; + bookmarks.build_bookmarks()?; Ok(bookmarks) } - pub fn get_bookmarks(&mut self) -> &mut HashMap { - &mut self.bookmarks - } - - pub fn add_bookmark(&mut self, name: &String, path: &PathBuf) -> Result<()> { - if !path.is_dir() { - return Err(Error::other( - "-- provided path argument does not point to a valid directory", - )); - } else { - self.bookmarks.insert(name.to_string(), path.to_path_buf()); - self.write_bookmark_file()?; - } - Ok(()) - } - - pub fn remove_bookmark(&mut self, name: &String) -> Result<()> { - if self.bookmarks.contains_key(name) { - _ = self.bookmarks.remove(name); - self.write_bookmark_file()?; - } else { - return Err(Error::other( - "-- bookmark requested to delete does not exist", - )); - } - Ok(()) - } - + /// reads and parses the configuration file fn build_config(&mut self) -> Result<()> { + Ok(()) + } + + /// reads and parses the bookmarks file + fn build_bookmarks(&mut self) -> Result<()> { let mut bookmark_file = self.conf_dir.clone(); bookmark_file.push(Self::BOOKMARK_FILE_NAME); @@ -100,6 +129,7 @@ impl Config { Ok(()) } + /// writes the bookmarks file fn write_bookmark_file(&self) -> Result<()> { let mut file_content = String::new(); for (mark, path) in self.bookmarks.iter() { @@ -112,4 +142,35 @@ impl Config { fs::write(path, file_content)?; Ok(()) } + + /// returns a mutable reference to self.bookmarks + pub fn get_bookmarks(&mut self) -> &mut HashMap { + &mut self.bookmarks + } + + /// adds a key/value pair to bookmarks and writes the bookmarks file + pub fn add_bookmark(&mut self, name: &String, path: &PathBuf) -> Result<()> { + if !path.is_dir() { + return Err(Error::other( + "-- provided path argument does not point to a valid directory", + )); + } else { + self.bookmarks.insert(name.to_string(), path.to_path_buf()); + self.write_bookmark_file()?; + } + Ok(()) + } + + /// removes a the entry with key=name if it exists, then writes the bookmarks file + pub fn remove_bookmark(&mut self, name: &String) -> Result<()> { + if self.bookmarks.contains_key(name) { + _ = self.bookmarks.remove(name); + self.write_bookmark_file()?; + } else { + return Err(Error::other( + "-- bookmark requested to delete does not exist", + )); + } + Ok(()) + } } diff --git a/src/format.rs b/src/format.rs index 62cf57e..97cafc3 100644 --- a/src/format.rs +++ b/src/format.rs @@ -1,162 +1,178 @@ use std::io::{Error, Result}; +pub const ESC: &'static str = "\x1B"; +pub const PREFIX: &'static str = "\x1B["; +pub const RESET_ARG: &'static str = "0"; +pub const TERMINATION: &'static str = "m"; +pub const RESET_SEQ: &'static str = "\x1B[0m"; +pub const FG: ColorContext = ColorContext::Foreground; +pub const BG: ColorContext = ColorContext::Background; -#[allow(dead_code)] -#[derive(Debug, Clone)] -pub struct Format { - /// escape character to start a sequence - pub escape: String, +pub const STYLES: Styles = Styles { + set: StyleCodes { + bold: "1", + dim: "2", + italic: "3", + underline: "4", + blink: "5", + reverse: "7", + invisible: "8", + strikethrough: "9", + }, + reset: StyleCodes { + bold: "22", + dim: "22", + italic: "23", + underline: "24", + blink: "25", + reverse: "27", + invisible: "28", + strikethrough: "29", + }, +}; - /// prefix = escape + '[' - pub prefix: String, - - /// reset both style and color - pub reset_all: String, - - /// text style - pub style: Styles, - - /// text color - pub color: Colors, -} +pub const COLORS: Colors = Colors { + fg: ColorCodes { + black: "30", + red: "31", + green: "32", + yellow: "33", + blue: "34", + magenta: "35", + cyan: "36", + white: "37", + extended: "38", + default: "39", + }, + bg: ColorCodes { + black: "40", + red: "41", + green: "42", + yellow: "43", + blue: "44", + magenta: "45", + cyan: "46", + white: "47", + extended: "48", + default: "49", + }, +}; #[allow(dead_code)] #[derive(Debug, Clone)] pub struct Styles { - /// set style pub set: StyleCodes, - - /// reset style pub reset: StyleCodes, } #[allow(dead_code)] #[derive(Debug, Clone)] pub struct StyleCodes { - pub bold: String, - pub dim: String, - pub italic: String, - pub underline: String, - pub blink: String, - pub reverse: String, - pub invisible: String, - pub strikethrough: String, + pub bold: &'static str, + pub dim: &'static str, + pub italic: &'static str, + pub underline: &'static str, + pub blink: &'static str, + pub reverse: &'static str, + pub invisible: &'static str, + pub strikethrough: &'static str, } #[allow(dead_code)] #[derive(Debug, Clone)] pub struct Colors { - pub foreground: ColorCodes, - pub background: ColorCodes, + pub fg: ColorCodes, + pub bg: ColorCodes, } #[allow(dead_code)] #[derive(Debug, Clone)] pub struct ColorCodes { - pub black: String, - pub red: String, - pub green: String, - pub yellow: String, - pub blue: String, - pub magenta: String, - pub cyan: String, - pub white: String, - pub default: String, + pub black: &'static str, + pub red: &'static str, + pub green: &'static str, + pub yellow: &'static str, + pub blue: &'static str, + pub magenta: &'static str, + pub cyan: &'static str, + pub white: &'static str, + pub extended: &'static str, + pub default: &'static str, } #[allow(dead_code)] -impl Format { - pub const ESC: &str = "\x1B"; - pub const PREFIX: &str = "\x1B["; - pub const TERMINATION: &str = "m"; - pub const RESET_STYLE: &str = "\x1B[0m"; +#[derive(Debug, Clone)] +pub enum ColorContext { + Foreground, + Background, +} - pub fn new() -> Format { - Format { - escape: String::from(Self::ESC), - prefix: String::from(Self::PREFIX), - reset_all: String::from("0"), - style: Styles { - set: StyleCodes { - bold: String::from("1"), - dim: String::from("2"), - italic: String::from("3"), - underline: String::from("4"), - blink: String::from("5"), - reverse: String::from("7"), - invisible: String::from("8"), - strikethrough: String::from("9"), - }, - reset: StyleCodes { - bold: String::from("22"), - dim: String::from("22"), - italic: String::from("23"), - underline: String::from("24"), - blink: String::from("25"), - reverse: String::from("27"), - invisible: String::from("28"), - strikethrough: String::from("29"), - }, - }, - color: Colors { - foreground: ColorCodes { - black: String::from("30"), - red: String::from("31"), - green: String::from("32"), - yellow: String::from("33"), - blue: String::from("34"), - magenta: String::from("35"), - cyan: String::from("36"), - white: String::from("37"), - default: String::from("39"), - }, - background: ColorCodes { - black: String::from("40"), - red: String::from("41"), - green: String::from("42"), - yellow: String::from("43"), - blue: String::from("44"), - magenta: String::from("45"), - cyan: String::from("46"), - white: String::from("47"), - default: String::from("49"), - }, - }, +/// generates a common style sequence of format +/// `\x1B[;;m` +/// all elements are optional, if none is supplied the function returns an error +pub fn generate_style_sequence( + style: Option>, + foreground: Option<&str>, + background: Option<&str>, +) -> String { + // assemble sequence as vector of string + // this way the semicolons to separate arguments + // can be inserted with 'join()' + let mut sequence = PREFIX.to_owned(); + let mut arguments = Vec::::new(); + if let Some(item) = style { + for entry in item { + arguments.push(entry.to_owned()); } } + if let Some(item) = foreground { + arguments.push(item.to_owned()); + } + if let Some(item) = background { + arguments.push(item.to_owned()); + } - pub fn generate_style_sequence( - &self, - style: Option<&Vec>, - foreground: Option<&String>, - background: Option<&String>, - ) -> Result { - if style.is_none() && foreground.is_none() && background.is_none() { - return Err(Error::other("-- generate_style_sequence called without arguments")); - } - - // assemble sequence if called with arguments - let mut sequence = Vec::::new(); - if let Some(item) = style { - sequence.push_str(item); - sequence.push(';'); - } - if let Some(item) = foreground { - sequence.push_str(item); - sequence.push(';'); - } - if let Some(item) = background { - sequence.push_str(item); - sequence.push(';'); - } - sequence.pop(); // remove final semicolon - sequence.push_str(Self::TERMINATION); // terminate sequence - - sequence + // panic if no arguments provided since this is a programming mistake + // which should not + if arguments.is_empty() { + panic!("no arguments provided to 'generate_style_sequence()'"); } + sequence.push_str(&arguments.join(";")); + sequence.push_str(TERMINATION); // terminate sequence with the termination character + sequence +} - pub fn generate_rgb_sequence(&self, ) -> String { +/// generates a 256 color sequence +/// see `generate_rgb_sequence(..)` for details +pub fn generate_256color_sequence(context: ColorContext, color: u8) -> String { + let mut sequence = PREFIX.to_owned(); + // choose context + match context { + ColorContext::Foreground => sequence.push_str(COLORS.fg.extended), + ColorContext::Background => sequence.push_str(COLORS.bg.extended), + }; + // make it a rgb sequence + sequence.push_str(";5;"); + sequence.push_str(&format!("{color}m")); + sequence +} - String::from("hi") - } +/// generates a rgb color sequence +/// **note**: not all terminal emulators support rgb colors +/// +/// rgb sequences are built the same as 256 color sequences: +/// `\x1B[;2;;;m` +/// where *context* is either '38' or '48' for foreground and background respectively +/// and *r,g,b* are the values of each color channel +pub fn generate_rgb_sequence(context: ColorContext, red: u8, green: u8, blue: u8) -> String { + let mut sequence = PREFIX.to_owned(); + // choose context + match context { + ColorContext::Foreground => sequence.push_str(COLORS.fg.extended), + ColorContext::Background => sequence.push_str(COLORS.bg.extended), + }; + // make it a rgb sequence + sequence.push_str(";2;"); + sequence.push_str(&format!("{red};{green};{blue}m")); + sequence } diff --git a/src/main.rs b/src/main.rs index 7b8a73d..1fa88fa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,8 +5,8 @@ mod stack; use arguments::*; use clap::Parser; -use config::Config; -use format::Format; +use config::*; +use format::*; use stack::Stack; use std::env::{current_dir, var}; use std::io::{Error, Result}; @@ -14,45 +14,51 @@ use std::path::{Path, PathBuf}; use std::str::FromStr; fn main() -> Result<()> { - let format: Format = format::Format::new(); + let style_error = + generate_style_sequence(Some(vec![STYLES.set.bold]), Some(COLORS.fg.red), None); let args = match Arguments::try_parse() { Ok(a) => a, Err(e) => { - print!( - "echo '{}{}m{}{}{}m'", - format.prefix, format.color.foreground.red, e, format.prefix, format.reset_all - ); + print!("echo '{}{}{}' && false", style_error, e, RESET_SEQ); + return Ok(()); + } + }; + let mut config = match Config::new() { + Ok(value) => value, + Err(error) => { + print!("echo '{}{}{}' && false", style_error, error, RESET_SEQ); return Ok(()); } }; let mut stack = match Stack::new(args.pid) { Ok(stack) => stack, Err(_) => { - print!("echo "); - return Err(Error::other("-- failed to build stack")); + print!( + "echo '{}-- failed to build stack{}' && false", + style_error, RESET_SEQ + ); + return Err(Error::other("")); } }; 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), + Action::push(push_args) => handle_push(&push_args, &mut config, &mut stack), + Action::pop(pop_args) => handle_pop(&pop_args, &mut config, &mut stack), + Action::stack(stack_args) => handle_stack(&stack_args, &mut config, &mut stack), + Action::bookmark(bookmark_args) => handle_bookmark(&bookmark_args, &mut config, &mut stack), }; if res.is_err() { print!( - "echo '{}{}m{}{}{}m'", - format.prefix, - format.color.foreground.red, + "echo '{}{}{}' && false", + style_error, res.unwrap_err(), - format.prefix, - format.reset_all + RESET_SEQ, ); } Ok(()) } -fn handle_push(args: &PushArgs, stack: &mut Stack) -> Result<()> { +fn handle_push(args: &PushArgs, _config: &mut Config, stack: &mut Stack) -> Result<()> { let path = match args.path.clone() { Some(value) => value, None => { @@ -70,7 +76,7 @@ fn handle_push(args: &PushArgs, stack: &mut Stack) -> Result<()> { Ok(()) } -fn handle_pop(args: &PopArgs, stack: &mut Stack) -> Result<()> { +fn handle_pop(args: &PopArgs, _config: &mut Config, stack: &mut Stack) -> Result<()> { let path = stack.pop_entry(args.num_entries)?; println!( "cd -- {}", @@ -82,10 +88,10 @@ fn handle_pop(args: &PopArgs, stack: &mut Stack) -> Result<()> { Ok(()) } -fn handle_stack(args: &StackArgs, stack: &mut Stack) -> Result<()> { +fn handle_stack(args: &StackArgs, config: &mut Config, stack: &mut Stack) -> Result<()> { if args.stack_action.is_some() { match args.stack_action.clone().unwrap() { - StackAction::clear(_) => return stack.clear_stack(), + StackAction::clear(_) => return stack.clear_stack(config), } } // retrieve stack @@ -94,11 +100,7 @@ fn handle_stack(args: &StackArgs, stack: &mut Stack) -> Result<()> { Ok(()) } -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())), - }; +fn handle_bookmark(args: &BookmarkArgs, config: &mut Config, stack: &mut Stack) -> Result<()> { // if args.bookmark_action.is_some() { if args.bookmark_action.is_some() { match args.bookmark_action.clone().unwrap() { @@ -109,7 +111,7 @@ fn handle_bookmark(args: &BookmarkArgs, stack: &mut Stack) -> Result<()> { } else if args.name.is_some() { let path = match config.get_bookmarks().get(args.name.as_ref().unwrap()) { Some(value) => value, - None => return Err(Error::other("requested bookmark does not exist")), + None => return Err(Error::other("-- requested bookmark does not exist")), }; push_path(path, stack)?; } else { diff --git a/src/stack.rs b/src/stack.rs index 31ee44e..3ca6d6f 100644 --- a/src/stack.rs +++ b/src/stack.rs @@ -4,7 +4,7 @@ use std::io::{Error, Result}; use std::path::{Path, PathBuf}; use std::str::FromStr; use sysinfo::{Pid, System}; -use super::config::Settings; +use super::config::*; #[derive(Debug, Clone)] pub struct Stack { @@ -39,8 +39,10 @@ impl Stack { } /// clear stack by deleting the associated stack file - pub fn clear_stack(&mut self) -> Result<()> { - fs::remove_file(self.path.clone()) + pub fn clear_stack(&mut self, config: &mut Config) -> Result<()> { + fs::remove_file(self.path.clone())?; + print!("echo '{}stack cleared successfully.{}'", config.settings.styles.note, config.settings.styles.reset); + Ok(()) } /// push entry to stack