diff --git a/rmenu/Cargo.toml b/rmenu/Cargo.toml index 6dbda0a..c9e14bc 100644 --- a/rmenu/Cargo.toml +++ b/rmenu/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" clap = { version = "4.3.15", features = ["derive"] } dioxus = "0.3.2" dioxus-desktop = "0.3.0" -dirs = "5.0.1" +env_logger = "0.10.0" heck = "0.4.1" keyboard-types = "0.6.2" log = "0.4.19" diff --git a/rmenu/src/config.rs b/rmenu/src/config.rs index 73be9a1..80f6722 100644 --- a/rmenu/src/config.rs +++ b/rmenu/src/config.rs @@ -74,14 +74,13 @@ impl<'de> Deserialize<'de> for Keybind { } #[derive(Debug, PartialEq, Deserialize)] +#[serde(default)] pub struct KeyConfig { pub exec: Vec, pub exit: Vec, pub move_up: Vec, pub move_down: Vec, - #[serde(default)] pub open_menu: Vec, - #[serde(default)] pub close_menu: Vec, } @@ -129,23 +128,19 @@ impl Default for WindowConfig { } #[derive(Debug, PartialEq, Deserialize)] +#[serde(default)] pub struct Config { - pub css: Vec, pub use_icons: bool, pub search_regex: bool, pub ignore_case: bool, - #[serde(default)] pub plugins: BTreeMap>, - #[serde(default)] pub keybinds: KeyConfig, - #[serde(default)] pub window: WindowConfig, } impl Default for Config { fn default() -> Self { Self { - css: vec![], use_icons: true, search_regex: false, ignore_case: true, diff --git a/rmenu/src/exec.rs b/rmenu/src/exec.rs index c481c51..703936f 100644 --- a/rmenu/src/exec.rs +++ b/rmenu/src/exec.rs @@ -5,6 +5,7 @@ use std::process::Command; use rmenu_plugin::Action; pub fn execute(action: &Action) { + log::info!("executing: {} {:?}", action.name, action.exec); let args = match shell_words::split(&action.exec) { Ok(args) => args, Err(err) => panic!("{:?} invalid command {err}", action.exec), diff --git a/rmenu/src/gui.rs b/rmenu/src/gui.rs index 38d83bf..bc37152 100644 --- a/rmenu/src/gui.rs +++ b/rmenu/src/gui.rs @@ -95,13 +95,8 @@ fn TableEntry<'a>(cx: Scope<'a, GEntry<'a>>) -> Element<'a> { ondblclick: |_| { let action = match cx.props.entry.actions.get(0) { Some(action) => action, - None => { - let name = &cx.props.entry.name; - log::warn!("no action to execute on {:?}", name); - return; - } + None => panic!("No Action Configured"), }; - log::info!("executing: {:?}", action.exec); execute(action); }, if cx.props.config.use_icons { @@ -168,7 +163,7 @@ fn App(cx: Scope) -> Element { // retrieve results build and build position-tracker let tracker = PosTracker::new(cx, results.clone()); let (pos, subpos) = tracker.position(); - log::debug!("pos: {pos}, {subpos}"); + log::debug!("search: {search:?}, pos: {pos}, {subpos}"); // keyboard events let keybinds = &cx.props.config.keybinds; diff --git a/rmenu/src/main.rs b/rmenu/src/main.rs index 26b069e..4bcdc3c 100644 --- a/rmenu/src/main.rs +++ b/rmenu/src/main.rs @@ -15,6 +15,11 @@ use clap::Parser; use rmenu_plugin::Entry; use thiserror::Error; +static CONFIG_DIR: &'static str = "~/.config/rmenu/"; +static DEFAULT_CSS: &'static str = "~/.config/rmenu/style.css"; +static DEFAULT_CONFIG: &'static str = "~/.config/rmenu/config.yaml"; +static DEFAULT_CSS_CONTENT: &'static str = include_str!("../public/default.css"); + #[derive(Debug, Clone)] pub enum Format { Json, @@ -80,7 +85,7 @@ pub struct Args { #[arg(short, long)] config: Option, #[arg(long)] - css: Vec, + css: Option, } impl Args { @@ -88,16 +93,7 @@ impl Args { fn config(&self) -> Result { let path = match &self.config { Some(path) => path.to_owned(), - None => match dirs::config_dir() { - Some(mut dir) => { - dir.push("rmenu"); - dir.push("config.yaml"); - dir.to_string_lossy().to_string() - } - None => { - return Err(RMenuError::HomeNotFound); - } - }, + None => shellexpand::tilde(DEFAULT_CONFIG).to_string(), }; log::debug!("loading config from {path:?}"); let cfg = match read_to_string(path) { @@ -140,7 +136,7 @@ impl Args { /// Load Entries From Specified Sources fn load_sources(&self, cfg: &config::Config) -> Result, RMenuError> { - println!("{cfg:?}"); + log::debug!("config: {cfg:?}"); // execute commands to get a list of entries let mut entries = vec![]; for plugin in self.run.iter() { @@ -188,15 +184,17 @@ impl Args { /// Load Application pub fn parse_app() -> Result { let args = Self::parse(); - let mut config = args.config()?; + let config = args.config()?; // load css files from settings - config.css.extend(args.css.clone()); - let mut css = vec![]; - for path in config.css.iter() { - let path = shellexpand::tilde(path).to_string(); - let src = read_to_string(path)?; - css.push(src); - } + let csspath = args.css.clone().unwrap_or_else(|| DEFAULT_CSS.to_owned()); + let csspath = shellexpand::tilde(&csspath).to_string(); + let css = match read_to_string(csspath) { + Ok(css) => css, + Err(err) => { + log::error!("failed to load css: {err:?}"); + DEFAULT_CSS_CONTENT.to_owned() + } + }; // load entries from configured sources let entries = match args.run.len() > 0 { true => args.load_sources(&config)?, @@ -204,7 +202,7 @@ impl Args { }; // generate app object return Ok(App { - css: css.join("\n"), + css, name: "rmenu".to_owned(), entries, config, @@ -215,13 +213,16 @@ impl Args { //TODO: improve search w/ modes? //TODO: improve looks and css -//TODO: config -// - default and cli accessable modules (instead of piped in) -// - should resolve arguments/paths with home expansion - -//TODO: add exit key (Esc by default?) - part of keybindings - fn main() -> Result<(), RMenuError> { + // enable log if env-var is present + if std::env::var("RUST_LOG").is_ok() { + env_logger::init(); + } + // change directory to configuration dir + let cfgdir = shellexpand::tilde(CONFIG_DIR).to_string(); + if let Err(err) = std::env::set_current_dir(&cfgdir) { + log::error!("failed to change directory: {err:?}"); + } // parse cli / config / application-settings let app = Args::parse_app()?; gui::run(app); diff --git a/rmenu/src/state.rs b/rmenu/src/state.rs index c9cac12..5cd7e9c 100644 --- a/rmenu/src/state.rs +++ b/rmenu/src/state.rs @@ -28,9 +28,9 @@ impl<'a> PosTracker<'a> { } /// Move X Primary Results Downwards pub fn move_down(&self, x: usize) { + let max = std::cmp::max(self.results.len(), 1); self.subpos.set(0); - self.pos - .modify(|v| std::cmp::min(v + x, self.results.len() - 1)) + self.pos.modify(|v| std::cmp::min(v + x, max - 1)) } /// Get Current Position/SubPosition pub fn position(&self) -> (usize, usize) { @@ -64,11 +64,12 @@ impl<'a> PosTracker<'a> { /// Move Down Once With Context of SubMenu pub fn shift_down(&self) { let index = *self.pos.get(); - let result = &self.results[index]; - let subpos = *self.subpos.get(); - if subpos > 0 && subpos < result.actions.len() - 1 { - self.subpos.modify(|v| v + 1); - return; + if let Some(result) = &self.results.get(index) { + let subpos = *self.subpos.get(); + if subpos > 0 && subpos < result.actions.len() - 1 { + self.subpos.modify(|v| v + 1); + return; + } } self.move_down(1) }