diff --git a/src/lib/config.rs b/src/lib/config.rs index 8fc6068..c837734 100644 --- a/src/lib/config.rs +++ b/src/lib/config.rs @@ -67,6 +67,9 @@ pub enum Mode { /// use worf as file browser File, + + /// Use is as calculator + Math, } #[derive(Debug, Error)] @@ -84,6 +87,7 @@ impl FromStr for Mode { "drun" => Ok(Mode::Drun), "dmenu" => Ok(Mode::Dmenu), "file" => Ok(Mode::File), + "math" => Ok(Mode::Math), "auto" => Ok(Mode::Auto), _ => Err(ArgsError::InvalidParameter( format!("{s} is not a valid argument show this, see help for details").to_owned(), diff --git a/src/lib/mode.rs b/src/lib/mode.rs index d8b993b..4bc327e 100644 --- a/src/lib/mode.rs +++ b/src/lib/mode.rs @@ -24,7 +24,7 @@ struct DRunCache { } #[derive(Clone)] -struct DRunProvider { +struct DRunProvider { items: Vec>, cache_path: Option, cache: HashMap, @@ -145,7 +145,7 @@ impl ItemProvider for DRunProvider { } #[derive(Clone)] -struct FileItemProvider { +struct FileItemProvider { last_result: Option>>, menu_item_data: T, } @@ -276,22 +276,78 @@ impl ItemProvider for FileItemProvider { } } +#[derive(Clone)] +struct MathProvider { + menu_item_data: T, +} + +impl MathProvider { + fn new(menu_item_data: T) -> Self { + Self { + menu_item_data, + } + } + + fn contains_math_functions_or_starts_with_number(input: &str) -> bool { + // Regex for function names (word boundaries to match whole words) + let math_functions = r"\b(sqrt|abs|exp|ln|sin|cos|tan|asin|acos|atan|atan2|sinh|cosh|tanh|asinh|acosh|atanh|floor|ceil|round|signum|min|max|pi|e)\b"; + + // Regex for strings that start with a number (including decimals) + let starts_with_number = r"^\s*[+-]?(\d+(\.\d*)?|\.\d+)"; + + let math_regex = Regex::new(math_functions).unwrap(); + let number_regex = Regex::new(starts_with_number).unwrap(); + + math_regex.is_match(input) || number_regex.is_match(input) + } +} + +impl ItemProvider for MathProvider { + fn get_elements(&mut self, search: Option<&str>) -> Vec> { + if let Some(search_text) = search { + let result = match meval::eval_str(search_text) { + Ok(result) => result.to_string(), + Err(e) => format!("failed to calculate {e:?}"), + }; + + let item = MenuItem { + label: result, + icon_path: None, + action: search.map(|s| s.to_string()), + sub_elements: vec![], + working_dir: None, + initial_sort_score: 0, + search_sort_score: 0.0, + data: Some(self.menu_item_data.clone()), + }; + + vec![item] + } else { + vec![] + } + } + + fn get_sub_elements(&mut self, item: &MenuItem) -> Option>> { + None + } +} + #[derive(Debug, Clone, PartialEq)] enum AutoRunType { Math, DRun, File, - Ssh, - WebSearch, - Emoji, - Run, + // Ssh, + // WebSearch, + // Emoji, + // Run, } #[derive(Clone)] struct AutoItemProvider { drun_provider: DRunProvider, file_provider: FileItemProvider, - last_result: Option>>, + math_provider: MathProvider, } impl AutoItemProvider { @@ -299,55 +355,26 @@ impl AutoItemProvider { AutoItemProvider { drun_provider: DRunProvider::new(AutoRunType::DRun), file_provider: FileItemProvider::new(AutoRunType::File), - last_result: None, + math_provider: MathProvider::new(AutoRunType::Math), } } } -fn contains_math_functions_or_starts_with_number(input: &str) -> bool { - // Regex for function names (word boundaries to match whole words) - let math_functions = r"\b(sqrt|abs|exp|ln|sin|cos|tan|asin|acos|atan|atan2|sinh|cosh|tanh|asinh|acosh|atanh|floor|ceil|round|signum|min|max|pi|e)\b"; - - // Regex for strings that start with a number (including decimals) - let starts_with_number = r"^\s*[+-]?(\d+(\.\d*)?|\.\d+)"; - - let math_regex = Regex::new(math_functions).unwrap(); - let number_regex = Regex::new(starts_with_number).unwrap(); - - math_regex.is_match(input) || number_regex.is_match(input) -} - impl ItemProvider for AutoItemProvider { fn get_elements(&mut self, search_opt: Option<&str>) -> Vec> { if let Some(search) = search_opt { let trimmed_search = search.trim(); if trimmed_search.is_empty() { self.drun_provider.get_elements(search_opt) - } else if contains_math_functions_or_starts_with_number(trimmed_search) { - let result = match meval::eval_str(trimmed_search) { - Ok(result) => result.to_string(), - Err(e) => format!("failed to calculate {e:?}"), - }; - - let item = MenuItem { - label: result, - icon_path: None, - action: Some(trimmed_search.to_owned()), - sub_elements: vec![], - working_dir: None, - initial_sort_score: 0, - search_sort_score: 0.0, - data: Some(AutoRunType::Math), - }; - - return vec![item]; + } else if MathProvider::::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("/") || trimmed_search.starts_with("~") { self.file_provider.get_elements(search_opt) } else { - return self.drun_provider.get_elements(search_opt); + self.drun_provider.get_elements(search_opt) } } else { self.drun_provider.get_elements(search_opt) @@ -448,6 +475,25 @@ pub fn file(config: &mut Config) -> Result<(), String> { Ok(()) } +pub fn math(config: &mut Config) -> Result<(), String> { + let provider = MathProvider::new("".to_owned()); + if config.prompt.is_none() { + config.prompt = Some("math".to_owned()); + } + + // todo ues a arc instead of cloning the config + let selection_result = gui::show(config.clone(), provider); + match selection_result { + Ok(_) => { + } + Err(_) => { + log::error!("No item selected"); + } + } + + Ok(()) +} + fn update_drun_cache_and_run( cache_path: Option, cache: &mut HashMap, diff --git a/src/main.rs b/src/main.rs index 196264b..85687c9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,6 +29,9 @@ fn main() -> anyhow::Result<()> { Mode::File => { mode::file(&mut config).map_err(|e| anyhow!(e))?; } + Mode::Math => { + mode::math(&mut config).map_err(|e| anyhow!(e))?; + } Mode::Auto => { mode::auto(&mut config)?; } @@ -39,84 +42,3 @@ fn main() -> anyhow::Result<()> { Err(anyhow!("No mode provided")) } } - -// -// fn main() -> anyhow::Result<()> { -// env_logger::Builder::new() -// // todo change to info as default -// .parse_filters(&std::env::var("RUST_LOG").unwrap_or_else(|_| "debug".to_owned())) -// .init(); -// let args = Args::parse(); -// -// let home_dir = std::env::var("HOME")?; -// let config_path = args.config.as_ref().map(|c| PathBuf::from(c)).unwrap_or_else(||{ -// std::env::var("XDG_CONF_HOME") -// .map_or( -// PathBuf::from(home_dir.clone()).join(".config"), -// |xdg_conf_home| PathBuf::from(&xdg_conf_home), -// ) -// .join("wofi")// todo change to ravi -// .join("config") -// }); -// -// let colors_dir = std::env::var("XDG_CACHE_HOME") -// .map_or( -// PathBuf::from(home_dir.clone()).join(".cache"), -// |xdg_conf_home| PathBuf::from(&xdg_conf_home), -// ) -// .join("wal") -// .join("colors"); -// -// let toml_content = fs::read_to_string(config_path)?; -// let config: Config = toml::from_str(&toml_content).unwrap_or_default(); -// -// -// -// gtk4::init()?; -// -// let application = Application::builder() -// .application_id("com.example.FirstGtkApp") -// .build(); -// -// application.connect_activate(|app| { -// let window = ApplicationWindow::builder() -// .application(app) -// .title("First GTK Program") -// .name("window") -// .default_width(config.x.clone().unwrap()) -// .default_height(config.y.clone().unwrap()) -// .resizable(false) -// .decorated(false) -// .build(); -// -// -// -// // Create a dialog window -// let dialog = Dialog::new(); -// dialog.set_title(Some("Custom Dialog")); -// dialog.set_default_size(300, 150); -// -// // Create a vertical box container for the dialog content -// let mut vbox =gtk4:: Box::new(Orientation::Horizontal, 10); -// -// // Add a label to the dialog -// let label = Label::new(Some("This is a custom dialog!")); -// vbox.append(&label); -// -// // Set the dialog content -// dialog.set_child(Some(&vbox)); -// -// // Show the dialog -// dialog.present(); -// }); -// -// let empty_array: [&str; 0] = [];; -// -// -// application.run_with_args(&empty_array); -// -// debug!("merged config result {:#?}", config); -// -// -// Ok(()) -// }