add support for location
This commit is contained in:
parent
3e34d809be
commit
2453bbaf72
7 changed files with 151 additions and 120 deletions
14
README.md
14
README.md
|
@ -17,7 +17,7 @@ Allow blur for Worf
|
|||
layerrule = blur, worf
|
||||
```
|
||||
|
||||
## Additional functionality compared to Wofi (planed)
|
||||
## Additional functionality compared to Wofi
|
||||
* Support passing 'hidden' parameters that are not visible in the launcher but will be returned to the application
|
||||
* Window switcher for hyprland
|
||||
* All arguments expect show are supported by config and args
|
||||
|
@ -32,6 +32,12 @@ layerrule = blur, worf
|
|||
* `label`: Allows styling the label
|
||||
* `row`: Allows styling to row, mainly used to disable hover effects
|
||||
|
||||
## Library
|
||||
|
||||
The launcher and UI can be used to build any launcher, as the ui, config and run logic is available as a separate crate.
|
||||
This library is not available publicly yet as the interface is not stable enough.
|
||||
|
||||
|
||||
## Breaking changes to Wofi
|
||||
* Runtime behaviour is not guaranteed to be the same and won't ever be, this includes error messages and themes.
|
||||
* Themes in general are mostly compatible. Worf is using the same entity ids,
|
||||
|
@ -39,6 +45,7 @@ layerrule = blur, worf
|
|||
* Configuration files are not 100% compatible, Worf is using toml files instead, for most part this only means strings have to be quoted
|
||||
* Color files are not supported
|
||||
* `line_wrap` is now called `line-wrap`
|
||||
* Wofi has a C-API, that is not and won't be supported.
|
||||
|
||||
## Dropped arguments
|
||||
* `mode`, use show
|
||||
|
@ -48,8 +55,3 @@ layerrule = blur, worf
|
|||
### Dropped configuration options
|
||||
* stylesheet -> use style instead
|
||||
* color / colors -> GTK4 does not support color files
|
||||
|
||||
|
||||
|
||||
## Not supported
|
||||
* Wofi has a C-API, that is not and won't be supported.
|
||||
|
|
12
examples/dmenu.sh
Executable file
12
examples/dmenu.sh
Executable file
|
@ -0,0 +1,12 @@
|
|||
#!/bin/bash
|
||||
|
||||
# A list of options, one per line
|
||||
options="Option 1
|
||||
Option 2
|
||||
Option 3"
|
||||
|
||||
# Pipe options to wofi and capture the selection
|
||||
selection=$(echo "$options" | cargo run -- --show dmenu)
|
||||
|
||||
# Do something with the selection
|
||||
echo "You selected: $selection"
|
|
@ -2,7 +2,7 @@ use std::path::PathBuf;
|
|||
use std::str::FromStr;
|
||||
use std::{env, fmt, fs};
|
||||
|
||||
use anyhow::{Error, anyhow};
|
||||
use anyhow::anyhow;
|
||||
use clap::{Parser, ValueEnum};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
@ -23,6 +23,14 @@ impl fmt::Display for ConfigurationError {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Serialize, Deserialize)]
|
||||
pub enum Anchor {
|
||||
Top,
|
||||
Left,
|
||||
Bottom,
|
||||
Right,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug, Serialize, Deserialize)]
|
||||
pub enum MatchMethod {
|
||||
Fuzzy,
|
||||
|
@ -85,6 +93,20 @@ pub enum ArgsError {
|
|||
InvalidParameter(String),
|
||||
}
|
||||
|
||||
impl FromStr for Anchor {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s.trim() {
|
||||
"top" => Ok(Anchor::Top),
|
||||
"left" => Ok(Anchor::Left),
|
||||
"bottom" => Ok(Anchor::Bottom),
|
||||
"right" => Ok(Anchor::Right),
|
||||
other => Err(format!("Invalid anchor: {}", other)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Mode {
|
||||
type Err = ArgsError;
|
||||
|
||||
|
@ -201,8 +223,11 @@ pub struct Config {
|
|||
#[clap(short = 'q', long = "parse-search")]
|
||||
pub parse_search: Option<bool>,
|
||||
|
||||
#[clap(short = 'l', long = "location")]
|
||||
pub location: Option<String>,
|
||||
/// set where the window is displayed.
|
||||
/// can be used to anchor a window to an edge by
|
||||
/// setting top,left for example
|
||||
#[clap(short = 'l', long = "location", value_delimiter = ',', value_parser = clap::builder::ValueParser::new(Anchor::from_str))]
|
||||
pub location: Option<Vec<Anchor>>,
|
||||
|
||||
#[clap(short = 'a', long = "no-actions")]
|
||||
pub no_actions: Option<bool>,
|
||||
|
@ -508,7 +533,6 @@ pub fn default_line_wrap() -> Option<WrapMode> {
|
|||
// max_lines = lines;
|
||||
// columns = strtol(config_get(config, "columns", "1"), NULL, 10);
|
||||
// sort_order = config_get_mnemonic(config, "sort_order", "default", 2, "default", "alphabetical");
|
||||
// line_wrap = config_get_mnemonic(config, "line_wrap", "off", 4, "off", "word", "char", "word_char") - 1;
|
||||
// bool global_coords = strcmp(config_get(config, "global_coords", "false"), "true") == 0;
|
||||
// hide_search = strcmp(config_get(config, "hide_search", "false"), "true") == 0;
|
||||
// char* search = map_get(config, "search");
|
||||
|
@ -700,9 +724,9 @@ pub fn load_config(args_opt: Option<Config>) -> Result<Config, ConfigurationErro
|
|||
Mode::Run => merge_result.prompt = Some("run".to_owned()),
|
||||
Mode::Drun => merge_result.prompt = Some("drun".to_owned()),
|
||||
Mode::Dmenu => merge_result.prompt = Some("dmenu".to_owned()),
|
||||
Mode::Math => merge_result.prompt = Some("math".to_owned()),
|
||||
Mode::File => merge_result.prompt = Some("file".to_owned()),
|
||||
Mode::Auto => merge_result.prompt = Some("auto".to_owned()),
|
||||
Mode::Math => merge_result.prompt = Some("math".to_owned()),
|
||||
Mode::File => merge_result.prompt = Some("file".to_owned()),
|
||||
Mode::Auto => merge_result.prompt = Some("auto".to_owned()),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,70 +1,26 @@
|
|||
use anyhow::anyhow;
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::{env, fs, string};
|
||||
|
||||
use freedesktop_file_parser::DesktopFile;
|
||||
use gdk4::Display;
|
||||
use gtk4::prelude::*;
|
||||
use gtk4::{IconLookupFlags, IconTheme, TextDirection};
|
||||
use home::home_dir;
|
||||
use log::{debug, info, warn};
|
||||
use log;
|
||||
use regex::Regex;
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::{env, fs, string};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum DesktopError {
|
||||
MissingIcon,
|
||||
}
|
||||
|
||||
//
|
||||
// #[derive(Clone)]
|
||||
// pub struct IconResolver {
|
||||
// cache: HashMap<String, String>,
|
||||
// }
|
||||
//
|
||||
// impl Default for IconResolver {
|
||||
// #[must_use]
|
||||
// fn default() -> IconResolver {
|
||||
// Self::new()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl IconResolver {
|
||||
// #[must_use]
|
||||
// pub fn new() -> IconResolver {
|
||||
// IconResolver {
|
||||
// cache: HashMap::new(),
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// pub fn icon_path_no_cache(&self, icon_name: &str) -> Result<String, DesktopError> {
|
||||
// let icon = fetch_icon_from_theme(icon_name)
|
||||
// .or_else(|_|
|
||||
// fetch_icon_from_common_dirs(icon_name)
|
||||
// .or_else(|_| default_icon()));
|
||||
//
|
||||
// icon
|
||||
// }
|
||||
//
|
||||
// pub fn icon_path(&mut self, icon_name: &str) -> String {
|
||||
// if let Some(icon_path) = self.cache.get(icon_name) {
|
||||
// return icon_path.to_owned();
|
||||
// }
|
||||
//
|
||||
// let icon = self.icon_path_no_cache(icon_name);
|
||||
//
|
||||
// self.cache
|
||||
// .entry(icon_name.to_owned())
|
||||
// .or_insert_with(|| icon.unwrap_or_default())
|
||||
// .to_owned()
|
||||
// }
|
||||
// }
|
||||
|
||||
/// # Errors
|
||||
///
|
||||
/// Will return `Err` if no icon can be found
|
||||
pub fn default_icon() -> Result<String, DesktopError> {
|
||||
fetch_icon_from_theme("image-missing").map_err(|e| DesktopError::MissingIcon)
|
||||
fetch_icon_from_theme("image-missing").map_err(|_| DesktopError::MissingIcon)
|
||||
}
|
||||
|
||||
fn fetch_icon_from_theme(icon_name: &str) -> Result<String, DesktopError> {
|
||||
|
@ -177,7 +133,7 @@ pub fn find_desktop_files() -> Vec<DesktopFile> {
|
|||
.filter_map(|icon_dir| find_file_case_insensitive(&icon_dir, regex))
|
||||
.flat_map(|desktop_files| {
|
||||
desktop_files.into_iter().filter_map(|desktop_file| {
|
||||
debug!("loading desktop file {desktop_file:?}");
|
||||
log::debug!("loading desktop file {desktop_file:?}");
|
||||
fs::read_to_string(desktop_file)
|
||||
.ok()
|
||||
.and_then(|content| freedesktop_file_parser::parse(&content).ok())
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use std::collections::HashMap;
|
||||
use std::ops::DerefMut;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
|
||||
|
@ -15,13 +14,17 @@ use gtk4::prelude::{
|
|||
ApplicationExt, ApplicationExtManual, BoxExt, EditableExt, FlowBoxChildExt, GestureSingleExt,
|
||||
GtkWindowExt, ListBoxRowExt, NativeExt, WidgetExt,
|
||||
};
|
||||
use gtk4::{Align, EventControllerKey, Expander, FlowBox, FlowBoxChild, GestureClick, Image, Label, ListBox, ListBoxRow, Ordering, PolicyType, ScrolledWindow, SearchEntry, Widget, gdk, NaturalWrapMode};
|
||||
use gtk4::{
|
||||
Align, EventControllerKey, Expander, FlowBox, FlowBoxChild, GestureClick, Image, Label,
|
||||
ListBox, ListBoxRow, NaturalWrapMode, Ordering, PolicyType, ScrolledWindow, SearchEntry,
|
||||
Widget, gdk,
|
||||
};
|
||||
use gtk4::{Application, ApplicationWindow, CssProvider, Orientation};
|
||||
use gtk4_layer_shell::{Edge, KeyboardMode, LayerShell};
|
||||
use log;
|
||||
|
||||
use crate::config;
|
||||
use crate::config::{Animation, Config, MatchMethod, WrapMode};
|
||||
use crate::config::{Anchor, Animation, Config, MatchMethod, WrapMode};
|
||||
|
||||
type ArcMenuMap<T> = Arc<Mutex<HashMap<FlowBoxChild, MenuItem<T>>>>;
|
||||
type ArcProvider<T> = Arc<Mutex<dyn ItemProvider<T>>>;
|
||||
|
@ -32,6 +35,17 @@ pub trait ItemProvider<T: std::clone::Clone> {
|
|||
fn get_sub_elements(&mut self, item: &MenuItem<T>) -> Option<Vec<MenuItem<T>>>;
|
||||
}
|
||||
|
||||
impl From<&Anchor> for Edge {
|
||||
fn from(value: &Anchor) -> Self {
|
||||
match value {
|
||||
Anchor::Top => Edge::Top,
|
||||
Anchor::Left => Edge::Left,
|
||||
Anchor::Bottom => Edge::Bottom,
|
||||
Anchor::Right => Edge::Right,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<config::Orientation> for Orientation {
|
||||
fn from(orientation: config::Orientation) -> Self {
|
||||
match orientation {
|
||||
|
@ -44,9 +58,9 @@ impl From<config::Orientation> for Orientation {
|
|||
impl From<&WrapMode> for NaturalWrapMode {
|
||||
fn from(value: &WrapMode) -> Self {
|
||||
match value {
|
||||
WrapMode::None => {NaturalWrapMode::None},
|
||||
WrapMode::Word => {NaturalWrapMode::Word},
|
||||
WrapMode::Inherit => {NaturalWrapMode::Inherit},
|
||||
WrapMode::None => NaturalWrapMode::None,
|
||||
WrapMode::Word => NaturalWrapMode::Word,
|
||||
WrapMode::Inherit => NaturalWrapMode::Inherit,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -141,8 +155,11 @@ fn build_ui<T, P>(
|
|||
window.set_namespace(Some("worf"));
|
||||
}
|
||||
|
||||
/// todo make this configurable
|
||||
//window.set_anchor(Edge::Top, true);
|
||||
config.location.as_ref().map(|location| {
|
||||
for anchor in location {
|
||||
window.set_anchor(anchor.into(), true);
|
||||
}
|
||||
});
|
||||
|
||||
let outer_box = gtk4::Box::new(config.orientation.unwrap().into(), 0);
|
||||
outer_box.set_widget_name("outer-box");
|
||||
|
@ -816,7 +833,7 @@ fn create_menu_row<T: Clone + 'static>(
|
|||
}
|
||||
|
||||
let label = Label::new(Some(menu_item.label.as_str()));
|
||||
let wrap_mode : NaturalWrapMode = if let Some(config_wrap) = &config.line_wrap {
|
||||
let wrap_mode: NaturalWrapMode = if let Some(config_wrap) = &config.line_wrap {
|
||||
config_wrap.into()
|
||||
} else {
|
||||
NaturalWrapMode::Word
|
||||
|
@ -951,7 +968,9 @@ fn percent_or_absolute(value: Option<&String>, base_value: i32) -> Option<i32> {
|
|||
|
||||
// highly unlikely that we are dealing with > i64 items
|
||||
#[allow(clippy::cast_possible_wrap)]
|
||||
pub fn sort_menu_items_alphabetically_honor_initial_score<T: std::clone::Clone>(items: &mut [MenuItem<T>]) {
|
||||
pub fn sort_menu_items_alphabetically_honor_initial_score<T: std::clone::Clone>(
|
||||
items: &mut [MenuItem<T>],
|
||||
) {
|
||||
let mut regular_score = items.len() as i64;
|
||||
items.sort_by(|l, r| l.label.cmp(&r.label));
|
||||
|
||||
|
|
100
src/lib/mode.rs
100
src/lib/mode.rs
|
@ -1,21 +1,39 @@
|
|||
use std::os::unix::prelude::CommandExt;
|
||||
use std::path::PathBuf;
|
||||
use std::process::{Command, Stdio};
|
||||
use std::{env, fmt, fs, io};
|
||||
|
||||
use anyhow::Context;
|
||||
use freedesktop_file_parser::EntryType;
|
||||
use regex::Regex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::config::{Config, expand_path};
|
||||
use crate::desktop::{
|
||||
default_icon, find_desktop_files, get_locale_variants, lookup_name_with_locale,
|
||||
};
|
||||
use crate::gui;
|
||||
use crate::gui::{ItemProvider, MenuItem};
|
||||
use crate::{config, desktop, gui};
|
||||
use anyhow::{Context, Error, anyhow};
|
||||
use freedesktop_file_parser::EntryType;
|
||||
use gtk4::Image;
|
||||
use libc::option;
|
||||
use regex::Regex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::os::unix::prelude::CommandExt;
|
||||
use std::path::PathBuf;
|
||||
use std::process::{Command, Stdio};
|
||||
use std::{env, fs, io};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ModeError {
|
||||
UpdateCacheError(String),
|
||||
MissingAction,
|
||||
RunError(String),
|
||||
MissingCache,
|
||||
}
|
||||
|
||||
impl fmt::Display for ModeError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
ModeError::UpdateCacheError(s) => write!(f, "UpdateCacheError {s}"),
|
||||
ModeError::MissingAction => write!(f, "MissingAction"),
|
||||
ModeError::RunError(s) => write!(f, "RunError, {s}"),
|
||||
ModeError::MissingCache => write!(f, "MissingCache"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||
struct DRunCache {
|
||||
|
@ -139,7 +157,7 @@ impl<T: Clone> ItemProvider<T> for DRunProvider<T> {
|
|||
self.items.clone()
|
||||
}
|
||||
|
||||
fn get_sub_elements(&mut self, item: &MenuItem<T>) -> Option<Vec<MenuItem<T>>> {
|
||||
fn get_sub_elements(&mut self, _: &MenuItem<T>) -> Option<Vec<MenuItem<T>>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
@ -283,9 +301,7 @@ struct MathProvider<T: Clone> {
|
|||
|
||||
impl<T: std::clone::Clone> MathProvider<T> {
|
||||
fn new(menu_item_data: T) -> Self {
|
||||
Self {
|
||||
menu_item_data,
|
||||
}
|
||||
Self { menu_item_data }
|
||||
}
|
||||
|
||||
fn contains_math_functions_or_starts_with_number(input: &str) -> bool {
|
||||
|
@ -327,7 +343,7 @@ impl<T: Clone> ItemProvider<T> for MathProvider<T> {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_sub_elements(&mut self, item: &MenuItem<T>) -> Option<Vec<MenuItem<T>>> {
|
||||
fn get_sub_elements(&mut self, _: &MenuItem<T>) -> Option<Vec<MenuItem<T>>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
@ -366,7 +382,9 @@ impl ItemProvider<AutoRunType> for AutoItemProvider {
|
|||
let trimmed_search = search.trim();
|
||||
if trimmed_search.is_empty() {
|
||||
self.drun_provider.get_elements(search_opt)
|
||||
} else if MathProvider::<AutoRunType>::contains_math_functions_or_starts_with_number(trimmed_search) {
|
||||
} else if MathProvider::<AutoRunType>::contains_math_functions_or_starts_with_number(
|
||||
trimmed_search,
|
||||
) {
|
||||
self.math_provider.get_elements(search_opt)
|
||||
} else if trimmed_search.starts_with("$")
|
||||
|| trimmed_search.starts_with("/")
|
||||
|
@ -392,7 +410,7 @@ impl ItemProvider<AutoRunType> for AutoItemProvider {
|
|||
/// # Errors
|
||||
///
|
||||
/// Will return `Err` if it was not able to spawn the process
|
||||
pub fn d_run(config: &Config) -> anyhow::Result<()> {
|
||||
pub fn d_run(config: &Config) -> Result<(), ModeError> {
|
||||
let provider = DRunProvider::new("".to_owned());
|
||||
let cache_path = provider.cache_path.clone();
|
||||
let mut cache = provider.cache.clone();
|
||||
|
@ -400,9 +418,7 @@ pub fn d_run(config: &Config) -> anyhow::Result<()> {
|
|||
// todo ues a arc instead of cloning the config
|
||||
let selection_result = gui::show(config.clone(), provider);
|
||||
match selection_result {
|
||||
Ok(s) => {
|
||||
update_drun_cache_and_run(cache_path, &mut cache, s)?;
|
||||
}
|
||||
Ok(s) => update_drun_cache_and_run(cache_path, &mut cache, s)?,
|
||||
Err(_) => {
|
||||
log::error!("No item selected");
|
||||
}
|
||||
|
@ -411,7 +427,7 @@ pub fn d_run(config: &Config) -> anyhow::Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn auto(config: &Config) -> anyhow::Result<()> {
|
||||
pub fn auto(config: &Config) -> Result<(), ModeError> {
|
||||
let provider = AutoItemProvider::new();
|
||||
let cache_path = provider.drun_provider.cache_path.clone();
|
||||
let mut cache = provider.drun_provider.cache.clone();
|
||||
|
@ -429,12 +445,9 @@ pub fn auto(config: &Config) -> anyhow::Result<()> {
|
|||
}
|
||||
AutoRunType::File => {
|
||||
if let Some(action) = selection_result.action {
|
||||
spawn_fork(&action, selection_result.working_dir.as_ref())?
|
||||
spawn_fork(&action, selection_result.working_dir.as_ref())?;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
todo!("not supported yet");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -446,7 +459,7 @@ pub fn auto(config: &Config) -> anyhow::Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn file(config: &Config) -> Result<(), String> {
|
||||
pub fn file(config: &Config) -> Result<(), ModeError> {
|
||||
let provider = FileItemProvider::new("".to_owned());
|
||||
|
||||
// todo ues a arc instead of cloning the config
|
||||
|
@ -454,7 +467,7 @@ pub fn file(config: &Config) -> Result<(), String> {
|
|||
match selection_result {
|
||||
Ok(s) => {
|
||||
if let Some(action) = s.action {
|
||||
spawn_fork(&action, s.working_dir.as_ref()).map_err(|e| e.to_string())?;
|
||||
spawn_fork(&action, s.working_dir.as_ref())?;
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
|
@ -465,14 +478,13 @@ pub fn file(config: &Config) -> Result<(), String> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn math(config: &Config) -> Result<(), String> {
|
||||
pub fn math(config: &Config) -> Result<(), ModeError> {
|
||||
let provider = MathProvider::new("".to_owned());
|
||||
|
||||
// todo ues a arc instead of cloning the config
|
||||
let selection_result = gui::show(config.clone(), provider);
|
||||
match selection_result {
|
||||
Ok(_) => {
|
||||
}
|
||||
Ok(_) => {}
|
||||
Err(_) => {
|
||||
log::error!("No item selected");
|
||||
}
|
||||
|
@ -481,11 +493,15 @@ pub fn math(config: &Config) -> Result<(), String> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn dmenu(_: &Config) -> Result<(), ModeError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_drun_cache_and_run<T: Clone>(
|
||||
cache_path: Option<PathBuf>,
|
||||
cache: &mut HashMap<String, i64>,
|
||||
selection_result: MenuItem<T>,
|
||||
) -> Result<(), Error> {
|
||||
) -> Result<(), ModeError> {
|
||||
if let Some(cache_path) = cache_path {
|
||||
*cache.entry(selection_result.label).or_insert(0) += 1;
|
||||
if let Err(e) = save_cache_file(&cache_path, &cache) {
|
||||
|
@ -496,7 +512,7 @@ fn update_drun_cache_and_run<T: Clone>(
|
|||
if let Some(action) = selection_result.action {
|
||||
spawn_fork(&action, selection_result.working_dir.as_ref())
|
||||
} else {
|
||||
Err(anyhow::anyhow!("cannot find drun action"))
|
||||
Err(ModeError::MissingAction)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -520,12 +536,13 @@ fn save_cache_file(path: &PathBuf, data: &HashMap<String, i64>) -> anyhow::Resul
|
|||
fs::write(path, toml_string).map_err(|e| anyhow::anyhow!(e))
|
||||
}
|
||||
|
||||
fn load_cache_file(cache_path: Option<&PathBuf>) -> anyhow::Result<HashMap<String, i64>> {
|
||||
fn load_cache_file(cache_path: Option<&PathBuf>) -> Result<HashMap<String, i64>, ModeError> {
|
||||
let Some(path) = cache_path else {
|
||||
return Err(anyhow!("Cache is missing"));
|
||||
return Err(ModeError::MissingCache);
|
||||
};
|
||||
|
||||
let toml_content = fs::read_to_string(path)?;
|
||||
let toml_content =
|
||||
fs::read_to_string(path).map_err(|e| ModeError::UpdateCacheError(format!("{e}")))?;
|
||||
let parsed: toml::Value = toml_content.parse().expect("Failed to parse TOML");
|
||||
|
||||
let mut result: HashMap<String, i64> = HashMap::new();
|
||||
|
@ -555,17 +572,18 @@ fn create_file_if_not_exists(path: &PathBuf) -> anyhow::Result<()> {
|
|||
}
|
||||
}
|
||||
|
||||
fn spawn_fork(cmd: &str, working_dir: Option<&String>) -> anyhow::Result<()> {
|
||||
fn spawn_fork(cmd: &str, working_dir: Option<&String>) -> Result<(), ModeError> {
|
||||
// todo fix actions ??
|
||||
// todo graphical disk map icon not working
|
||||
|
||||
let parts = cmd.split(' ').collect::<Vec<_>>();
|
||||
if parts.is_empty() {
|
||||
return Err(anyhow!("empty command passed"));
|
||||
return Err(ModeError::MissingAction);
|
||||
}
|
||||
|
||||
if let Some(dir) = working_dir {
|
||||
env::set_current_dir(dir)?;
|
||||
env::set_current_dir(dir)
|
||||
.map_err(|e| ModeError::RunError(format!("cannot set workdir {e}")))?
|
||||
}
|
||||
|
||||
let exec = parts[0].replace('"', "");
|
||||
|
|
|
@ -13,7 +13,7 @@ fn main() -> anyhow::Result<()> {
|
|||
.init();
|
||||
|
||||
let args = config::parse_args();
|
||||
let mut config = config::load_config(Some(args)).map_err(|e| anyhow!(e))?;
|
||||
let config = config::load_config(Some(args)).map_err(|e| anyhow!(e))?;
|
||||
|
||||
if let Some(show) = &config.show {
|
||||
match show {
|
||||
|
@ -21,10 +21,10 @@ fn main() -> anyhow::Result<()> {
|
|||
todo!("run not implemented")
|
||||
}
|
||||
Mode::Drun => {
|
||||
mode::d_run(&config)?;
|
||||
mode::d_run(&config).map_err(|e| anyhow!(e))?;
|
||||
}
|
||||
Mode::Dmenu => {
|
||||
todo!("dmenu not implemented")
|
||||
mode::dmenu(&config).map_err(|e| anyhow!(e))?;
|
||||
}
|
||||
Mode::File => {
|
||||
mode::file(&config).map_err(|e| anyhow!(e))?;
|
||||
|
@ -33,7 +33,7 @@ fn main() -> anyhow::Result<()> {
|
|||
mode::math(&config).map_err(|e| anyhow!(e))?;
|
||||
}
|
||||
Mode::Auto => {
|
||||
mode::auto(&config)?;
|
||||
mode::auto(&config).map_err(|e| anyhow!(e))?;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue