diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 0000000..6eb63d0 --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,26 @@ +name: Rust + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Format + run: cargo fmt --check + - name: Clippy + run: cargo clippy + - name: Build + run: cargo build --verbose + - name: Run tests + run: cargo test --verbose diff --git a/src/args.rs b/src/args.rs index 608a9af..21a8011 100644 --- a/src/args.rs +++ b/src/args.rs @@ -1,9 +1,8 @@ -use std::str::FromStr; use clap::Parser; use serde::{Deserialize, Serialize}; +use std::str::FromStr; use thiserror::Error; - // Define a custom error type using the `thiserror` crate #[derive(Debug, Error)] pub enum ArgsError { @@ -19,18 +18,20 @@ pub enum Mode { Drun, /// reads from stdin and displays options which when selected will be output to stdout. - Dmenu + Dmenu, } impl FromStr for Mode { type Err = ArgsError; fn from_str(s: &str) -> Result { - match s { + match s { "run" => Ok(Mode::Run), "drun" => Ok(Mode::Drun), "dmenu" => Ok(Mode::Dmenu), - _ => Err(ArgsError::InvalidParameter(format!("{s} is not a valid argument show this, see help for details").to_owned())) + _ => Err(ArgsError::InvalidParameter( + format!("{s} is not a valid argument show this, see help for details").to_owned(), + )), } } } @@ -38,7 +39,6 @@ impl FromStr for Mode { #[derive(Parser, Debug, Deserialize, Serialize)] #[clap(about = "Ravi is a wofi clone written in rust, it aims to be a drop in replacement")] pub struct Args { - /// Forks the menu so you can close the terminal #[clap(short = 'f', long = "fork")] fork: bool, diff --git a/src/config.rs b/src/config.rs index 62fdfe5..775c6ba 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,9 +1,9 @@ +use crate::args::Args; use anyhow::anyhow; use gtk4::prelude::ToValue; use merge::Merge; use serde::{Deserialize, Serialize}; use serde_json::Value; -use crate::args::Args; #[derive(Debug, Deserialize, Serialize, Merge, Clone)] pub struct Config { @@ -77,7 +77,7 @@ pub struct Config { impl Default for Config { fn default() -> Self { - Config{ + Config { style: None, stylesheet: None, color: None, @@ -236,7 +236,6 @@ fn default_password_char() -> Option { Some("*".to_owned()) } - pub fn merge_config_with_args(config: &mut Config, args: &Args) -> anyhow::Result { let args_json = serde_json::to_value(args)?; let mut config_json = serde_json::to_value(config)?; diff --git a/src/desktop/mod.rs b/src/desktop/mod.rs index 62fe863..c3120c2 100644 --- a/src/desktop/mod.rs +++ b/src/desktop/mod.rs @@ -46,24 +46,23 @@ pub fn default_icon() -> String { } fn fetch_icon_from_desktop_file(icon_name: &str) -> Option { - find_desktop_files().into_iter() - .find_map(|desktop_file| { - desktop_file - .get("Desktop Entry") - .filter(|desktop_entry| { - desktop_entry - .get("Exec") - .and_then(|opt| opt.as_ref()) - .is_some_and(|exec| exec.to_lowercase().contains(icon_name)) - }) - .map(|desktop_entry| { - desktop_entry - .get("Icon") - .and_then(|opt| opt.as_ref()) - .map(ToOwned::to_owned) - .unwrap_or_default() - }) - }) + find_desktop_files().into_iter().find_map(|desktop_file| { + desktop_file + .get("Desktop Entry") + .filter(|desktop_entry| { + desktop_entry + .get("Exec") + .and_then(|opt| opt.as_ref()) + .is_some_and(|exec| exec.to_lowercase().contains(icon_name)) + }) + .map(|desktop_entry| { + desktop_entry + .get("Icon") + .and_then(|opt| opt.as_ref()) + .map(ToOwned::to_owned) + .unwrap_or_default() + }) + }) } fn fetch_icon_from_theme(icon_name: &str) -> Option { @@ -143,7 +142,7 @@ pub(crate) fn find_desktop_files() -> Vec = paths + let p: Vec<_> = paths .into_iter() .filter(|icon_dir| icon_dir.exists()) .filter_map(|icon_dir| { @@ -154,6 +153,7 @@ pub(crate) fn find_desktop_files() -> Vec, pub action: Box, pub sub_elements: Option>, - } pub fn init(config: Config, elements: Vec) -> anyhow::Result<()> { @@ -33,7 +38,6 @@ pub fn init(config: Config, elements: Vec) -> anyhow::Result<()> { gtk4::STYLE_PROVIDER_PRIORITY_APPLICATION, ); - let display = Display::default().expect("Could not connect to a display"); // Apply CSS to the display gtk4::style_context_add_provider_for_display( @@ -43,9 +47,7 @@ pub fn init(config: Config, elements: Vec) -> anyhow::Result<()> { ); // No need for application_id unless you want portal support - let app = Application::builder() - .application_id("ravi") - .build(); + let app = Application::builder().application_id("ravi").build(); app.connect_activate(move |app| { // Create a toplevel undecorated window @@ -141,8 +143,6 @@ pub fn init(config: Config, elements: Vec) -> anyhow::Result<()> { window.show(); - - // Get the display where the window resides let display = window.display(); @@ -151,17 +151,22 @@ pub fn init(config: Config, elements: Vec) -> anyhow::Result<()> { let monitor = display.monitor_at_surface(&surface); if let Some(monitor) = monitor { let geometry = monitor.geometry(); - if let Some(w) = percent_or_absolute(&config.width.clone().unwrap_or("800".to_owned()), geometry.width()) { + if let Some(w) = percent_or_absolute( + &config.width.clone().unwrap_or("800".to_owned()), + geometry.width(), + ) { window.set_width_request(w); } - if let Some(h) = percent_or_absolute(&config.height.clone().unwrap_or("500".to_owned()), geometry.height()) { + if let Some(h) = percent_or_absolute( + &config.height.clone().unwrap_or("500".to_owned()), + geometry.height(), + ) { window.set_height_request(h); } } else { error!("failed to get monitor to init window size"); } }); - }); let empty_array: [&str; 0] = []; @@ -233,9 +238,9 @@ fn percent_or_absolute(value: &String, base_value: i32) -> Option { let value = value.trim(); match value.parse::() { Ok(n) => { - let result = ((n as f32/ 100.0) * base_value as f32) as i32; + let result = ((n as f32 / 100.0) * base_value as f32) as i32; Some(result) - }, + } Err(_) => None, } } else { diff --git a/src/main.rs b/src/main.rs index a7194b2..fd62960 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ use crate::args::{Args, Mode}; use crate::config::Config; +use crate::desktop::find_desktop_files; use crate::gui::EntryElement; use clap::Parser; use gdk4::prelude::Cast; @@ -18,7 +19,6 @@ use std::os::unix::process::CommandExt; use std::path::PathBuf; use std::process::{Command, Stdio}; use std::sync::Arc; -use crate::desktop::find_desktop_files; mod args; mod config; @@ -65,11 +65,14 @@ fn main() -> anyhow::Result<()> { match args.mode { Mode::Run => {} Mode::Drun => { - let mut entries : Vec = Vec::new(); + let mut entries: Vec = Vec::new(); for file in &find_desktop_files() { if let Some(desktop_entry) = file.get("desktop entry") { - let icon = desktop_entry.get("icon").and_then(|x| x.as_ref().map(|x| x.to_owned())); - let Some(exec) = desktop_entry.get("exec").and_then(|x| x.as_ref().cloned()) else { + let icon = desktop_entry + .get("icon") + .and_then(|x| x.as_ref().map(|x| x.to_owned())); + let Some(exec) = desktop_entry.get("exec").and_then(|x| x.as_ref().cloned()) + else { continue; }; @@ -87,7 +90,9 @@ fn main() -> anyhow::Result<()> { }) }; - let name = desktop_entry.get("name").and_then(|x| x.as_ref().map(|x| x.to_owned())); + let name = desktop_entry + .get("name") + .and_then(|x| x.as_ref().map(|x| x.to_owned())); if let Some(name) = name { entries.push({ EntryElement { @@ -100,7 +105,7 @@ fn main() -> anyhow::Result<()> { } } } - entries.sort_by(|l, r|l.label.cmp(&r.label)); + entries.sort_by(|l, r| l.label.cmp(&r.label)); if config.prompt.is_none() { config.prompt = Some("dmenu".to_owned()); }