Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Default Modifier #23

Merged
merged 30 commits into from
Oct 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
c944c04
feat: Add default modifier to config struct
SergioRibera Sep 18, 2022
fa15a03
feat: Add test to new parse config
SergioRibera Sep 18, 2022
1ba8c6b
feat: make better test
SergioRibera Sep 18, 2022
060a506
feat: fix readme
SergioRibera Sep 18, 2022
9d5a0ca
feat: add more test for parser
SergioRibera Sep 18, 2022
27a9b35
feat: add subkeybind validation test
SergioRibera Sep 18, 2022
a961aec
feat: remove print lines from test and add chord command test
SergioRibera Sep 18, 2022
a731958
feat: remove print lines from test
SergioRibera Sep 18, 2022
a801d6e
fix: clippy warnings
SergioRibera Sep 18, 2022
7209fea
fix: clippy warnings
SergioRibera Sep 18, 2022
244f355
Move individual tests to test file
SergioRibera Oct 5, 2022
d3363e8
fix clippy warnings and format
SergioRibera Oct 5, 2022
b3e5ffd
feat: Impl TryFrom for Config from String
SergioRibera Oct 16, 2022
1326789
fix: Fix tests, change from_string to try_from
SergioRibera Oct 16, 2022
a99d83c
make clippy (pedantic) happy
VuiMuich Dec 14, 2022
d435150
Merge branch 'main' into feature/modifier_optional
VuiMuich Dec 14, 2022
7b6cb85
fix remaining merge issues
VuiMuich Dec 14, 2022
6dcabd2
Merge branch 'main' into feature/modifier_optional
VuiMuich Dec 14, 2022
2231e69
Merge branch 'clippy' into feature/modifier_optional
VuiMuich Dec 14, 2022
5b29df7
make clippy happy again
VuiMuich Dec 14, 2022
55683d1
fix part of the tests
VuiMuich Dec 15, 2022
ceee98e
alias media keys
VuiMuich Jun 27, 2023
3c38717
clippy auto fix
VuiMuich Jun 27, 2023
38d254f
bump `lefthk-core` to `0.2.0` to better reflect breaking changes
VuiMuich Jun 28, 2023
5ae6b1a
Merge branch 'main' into main
mautamu Oct 21, 2023
b121f14
Merge branch 'main' into feature/modifier_optional
mautamu Oct 21, 2023
139c932
Merge branch 'main' into feature/modifier_optional
VuiMuich Oct 23, 2023
3310bc4
fix fmt and clippy, merge main
VuiMuich Oct 23, 2023
5862ea0
fix: clippy test
SergioRibera Oct 24, 2023
d29aa0d
fix: format style
SergioRibera Oct 24, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ LeftHK - A hotkey daemon written in Rust
The configuration file should be created in ~/.config/lefthk/ and called config.ron. If the configuration file is not created the program will exit.
Example config:
```ron
#![enable(implicit_some)]
Config(
default_modifier: ["Mod4", "Shift"],
keybinds: [
Keybind(
command: Execute("st -e htop"),
modifier: ["Mod4", "Shift"],
key: Key("x"),
),
Keybind(
command: Executes(["st -e htop", "st -e bpytop"]),
modifier: ["Mod4", "Shift"],
key: Keys(["x", "m"]),
),
Keybind(
Expand Down
2 changes: 1 addition & 1 deletion lefthk/src/config/command.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use serde::{Deserialize, Serialize};

use super::keybind::Keybind;
use crate::config::keybind::Keybind;

#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
pub enum Command {
Expand Down
144 changes: 70 additions & 74 deletions lefthk/src/config/keybind.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
use crate::errors::{LeftError, Result};
use lefthk_core::config::command as command_mod;
use lefthk_core::config::Command as core_command;
use lefthk_core::config::Keybind as core_keybind;
use serde::Deserialize;
use serde::Serialize;
use lefthk_core::config::{
command as command_mod, Command as core_command, Keybind as core_keybind,
};
use serde::{Deserialize, Serialize};

use super::{command::Command, key::Key};

use std::convert::TryFrom;
use crate::config::{command::Command, key::Key};

macro_rules! get_key {
($expr:expr $(,)?) => {
Expand All @@ -32,75 +29,74 @@ pub type Keybinds = Vec<Keybind>;
#[derive(Debug, PartialEq, Clone, Eq, Serialize, Deserialize)]
pub struct Keybind {
pub command: Command,
pub modifier: Vec<String>,
pub modifier: Option<Vec<String>>,
pub key: Key,
}

impl TryFrom<Keybind> for Vec<core_keybind> {
type Error = LeftError;

fn try_from(kb: Keybind) -> Result<Self> {
let command_key_pairs: Vec<(Box<dyn core_command>, String)> = match kb.command {
Command::Chord(children) if !children.is_empty() => {
let key = get_key!(kb.key);
let children = children
.iter()
.filter_map(|kb| match TryFrom::try_from(kb.clone()) {
Ok(keybinds) => Some::<Vec<lefthk_core::config::Keybind>>(keybinds),
Err(err) => {
tracing::error!("Invalid key binding: {}\n{:?}", err, kb);
None
}
})
.flatten()
.collect();
pub(crate) fn try_from(kb: Keybind, default_modifier: &[String]) -> Result<Vec<core_keybind>> {
let command_key_pairs: Vec<(Box<dyn core_command>, String)> = match kb.command {
Command::Chord(children) if !children.is_empty() => {
let key = get_key!(kb.key);
let children = children
.iter()
.filter_map(|kb| match try_from(kb.clone(), default_modifier) {
Ok(keybinds) => Some::<Vec<lefthk_core::config::Keybind>>(keybinds),
Err(err) => {
tracing::error!("Invalid key binding: {}\n{:?}", err, kb);
None
}
})
.flatten()
.collect();

vec![(Box::new(command_mod::Chord::new(children)), key)]
}
Command::Chord(_) => return Err(LeftError::ChildrenNotFound),
Command::Execute(value) if !value.is_empty() => {
let keys = get_key!(kb.key);
vec![(Box::new(command_mod::Execute::new(&value)), keys)]
}
Command::Execute(_) => return Err(LeftError::ValueNotFound),
Command::Executes(values) if !values.is_empty() => {
let keys = get_keys!(kb.key);
if keys.len() != values.len() {
return Err(LeftError::NumberOfKeysDiffersFromValues);
}
values
.iter()
.enumerate()
.map(|(i, v)| {
(
Box::new(command_mod::Execute::new(&v)) as Box<dyn core_command>,
keys[i].clone(),
)
})
.collect()
}
Command::Executes(_) => return Err(LeftError::ValuesNotFound),
Command::ExitChord => {
let keys = get_key!(kb.key);
vec![(Box::new(command_mod::ExitChord::new()), keys)]
}
Command::Reload => {
let keys = get_key!(kb.key);
vec![(Box::new(command_mod::Reload::new()), keys)]
}
Command::Kill => {
let keys = get_key!(kb.key);
vec![(Box::new(command_mod::Kill::new()), keys)]
vec![(Box::new(command_mod::Chord::new(children)), key)]
}
Command::Chord(_) => return Err(LeftError::ChildrenNotFound),
Command::Execute(value) if !value.is_empty() => {
let keys = get_key!(kb.key);
vec![((Box::new(command_mod::Execute::new(&value))), keys)]
}
Command::Execute(_) => return Err(LeftError::ValueNotFound),
Command::Executes(values) if !values.is_empty() => {
let keys = get_keys!(kb.key);
if keys.len() != values.len() {
return Err(LeftError::NumberOfKeysDiffersFromValues);
}
};
let keybinds = command_key_pairs
.iter()
.map(|(c, k)| core_keybind {
command: c.normalize(),
modifier: kb.modifier.clone(),
key: k.clone(),
})
.collect();
Ok(keybinds)
}
values
.iter()
.enumerate()
.map(|(i, v)| {
(
Box::new(command_mod::Execute::new(&v)) as Box<dyn core_command>,
keys[i].clone(),
)
})
.collect()
}
Command::Executes(_) => return Err(LeftError::ValuesNotFound),
Command::ExitChord => {
let keys = get_key!(kb.key);
vec![((Box::new(command_mod::ExitChord::new())), keys)]
}
Command::Reload => {
let keys = get_key!(kb.key);
vec![((Box::new(command_mod::Reload::new())), keys)]
}
Command::Kill => {
let keys = get_key!(kb.key);
vec![((Box::new(command_mod::Kill::new())), keys)]
}
};
let keybinds = command_key_pairs
.iter()
.map(|(c, k)| core_keybind {
command: c.normalize(),
modifier: kb
.modifier
.clone()
.unwrap_or_else(|| default_modifier.to_vec()),
key: k.clone(),
})
.collect();
Ok(keybinds)
}
51 changes: 32 additions & 19 deletions lefthk/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ pub mod keybind;
use crate::errors::{LeftError, Result};

use serde::{Deserialize, Serialize};
use std::{convert::TryFrom, fs, path::Path};
use std::{fs, path::Path};
use xdg::BaseDirectories;

use self::{
Expand All @@ -15,35 +15,35 @@ use self::{

#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
pub struct Config {
pub(crate) default_modifier: Vec<String>,
keybinds: Keybinds,
}

impl lefthk_core::config::Config for Config {
fn mapped_bindings(&self) -> Vec<lefthk_core::config::Keybind> {
self.keybinds
.iter()
.filter_map(|kb| match TryFrom::try_from(kb.clone()) {
Ok(keybinds) => Some::<Vec<lefthk_core::config::Keybind>>(keybinds),
Err(err) => {
tracing::error!("Invalid key binding: {}\n{:?}", err, kb);
None
}
})
.filter_map(
|kb| match keybind::try_from(kb.clone(), &self.default_modifier.clone()) {
Ok(keybinds) => Some::<Vec<lefthk_core::config::Keybind>>(keybinds),
Err(err) => {
tracing::error!("Invalid key binding: {}\n{:?}", err, kb);
None
}
},
)
.flatten()
.collect()
}
}

/// # Errors
///
/// Thes will error when no config file is found, most propably as system or
/// user error for provideng a wrong path
pub fn load() -> Result<Config> {
let path = BaseDirectories::with_prefix(lefthk_core::LEFTHK_DIR_NAME)?;
fs::create_dir_all(path.get_config_home())?;
let file_name = path.place_config_file("config.ron")?;
if Path::new(&file_name).exists() {
let contents = fs::read_to_string(file_name)?;
impl TryFrom<String> for Config {
type Error = LeftError;
/// # Errors
///
/// Thes will error when no config file is found, most propably as system or
/// user error for provideng a wrong path
fn try_from(contents: String) -> Result<Self> {
let mut config: Config = ron::from_str(&contents)?;
let global_exit_chord = config
.keybinds
Expand All @@ -57,7 +57,20 @@ pub fn load() -> Result<Config> {
.collect();
propagate_exit_chord(chords, &global_exit_chord);

return Ok(config);
Ok(config)
}
}

/// # Errors
///
/// This errors, when no Config is found at the path
pub fn load() -> Result<Config> {
let path = BaseDirectories::with_prefix(lefthk_core::LEFTHK_DIR_NAME)?;
fs::create_dir_all(path.get_config_home())?;
let file_name = path.place_config_file("config.ron")?;
if Path::new(&file_name).exists() {
let contents = fs::read_to_string(file_name)?;
Config::try_from(contents)?;
}
Err(LeftError::NoConfigFound)
}
Expand Down
17 changes: 10 additions & 7 deletions lefthk/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
use crate::errors::LeftError;
use clap::{App, Arg};
use lefthk_core::config::{command, Command};
use lefthk_core::ipc::Pipe;
use lefthk_core::worker::Status;
use lefthk_core::{config::Config, worker::Worker};
use std::fs;
use std::io::Write;
use std::sync::atomic::{AtomicBool, Ordering};
use lefthk_core::{
config::{command, Command, Config},
ipc::Pipe,
worker::{Status, Worker},
};
use std::{
fs,
io::Write,
sync::atomic::{AtomicBool, Ordering},
};
use xdg::BaseDirectories;

use tracing_subscriber::{filter::EnvFilter, filter::LevelFilter, fmt, layer::SubscriberExt};
Expand Down
Loading
Loading