Skip to content

Commit

Permalink
Make twix user config keys optional (#1527)
Browse files Browse the repository at this point in the history
  • Loading branch information
rmburg authored Nov 30, 2024
1 parent 92bfdc8 commit 1559bc1
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 14 deletions.
70 changes: 60 additions & 10 deletions tools/twix/src/configuration.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
pub mod keybind_plugin;
pub mod keys;
pub mod merge;

use std::path::{Path, PathBuf};

use merge::Merge;
use serde::Deserialize;
use thiserror::Error;

pub mod keybind_plugin;
pub mod keys;

const DEFAULT_CONFIG: &str = include_str!("../config_default.toml");

#[derive(Debug, Error)]
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("IO error: {0}")]
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("TOML parsing error: {0}")]
Parsing(#[from] toml::de::Error),
Expand All @@ -29,6 +30,11 @@ pub struct Configuration {
pub keys: keys::Keybinds,
}

#[derive(Debug, Deserialize)]
pub struct RawConfiguration {
pub keys: Option<keys::Keybinds>,
}

impl Configuration {
pub fn load() -> Result<Self, Error> {
Self::load_from_file(config_path())
Expand All @@ -38,7 +44,7 @@ impl Configuration {
match std::fs::read_to_string(&path) {
Ok(config_file) => {
let mut configuration = Self::default();
let user_configuration: Configuration = toml::from_str(&config_file)?;
let user_configuration: RawConfiguration = toml::from_str(&config_file)?;

configuration.merge(user_configuration);

Expand All @@ -54,9 +60,19 @@ impl Configuration {
}
}
}
}

impl Merge<RawConfiguration> for Configuration {
fn merge(&mut self, other: RawConfiguration) {
let RawConfiguration { keys } = other;

self.keys.merge(keys);
}
}

pub fn merge(&mut self, other: Self) {
let Self { keys } = other;
impl Merge<Configuration> for Configuration {
fn merge(&mut self, other: Configuration) {
let Configuration { keys } = other;

self.keys.merge(keys);
}
Expand All @@ -70,7 +86,9 @@ impl Default for Configuration {

#[cfg(test)]
mod tests {
use super::{Configuration, DEFAULT_CONFIG};
use crate::configuration::merge::Merge;

use super::{Configuration, RawConfiguration, DEFAULT_CONFIG};

#[test]
fn parse_default_config() {
Expand Down Expand Up @@ -112,4 +130,36 @@ mod tests {
.unwrap()
);
}

#[test]
fn merge_partial_config() {
let mut default_config: Configuration = toml::from_str(
r#"
[keys]
C-a = "focus_left"
C-S-a = "reconnect"
"#,
)
.unwrap();

let user_config: RawConfiguration = toml::from_str(
r#"
"#,
)
.unwrap();

default_config.merge(user_config);

assert_eq!(
default_config,
toml::from_str(
r#"
[keys]
C-a = "focus_left"
C-S-a = "reconnect"
"#,
)
.unwrap()
);
}
}
12 changes: 8 additions & 4 deletions tools/twix/src/configuration/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ use serde::{
};
use thiserror::Error;

use super::merge::Merge;

#[cfg_attr(test, derive(PartialEq))]
#[derive(Debug, Error)]
pub enum Error {
Expand Down Expand Up @@ -144,15 +146,17 @@ impl Keybinds {
}
}

pub fn merge(&mut self, other: Self) {
self.keybinds.extend(other.keybinds);
}

pub fn iter(&self) -> impl Iterator<Item = (&KeybindTrigger, &KeybindAction)> {
self.keybinds.iter()
}
}

impl Merge<Self> for Keybinds {
fn merge(&mut self, other: Self) {
self.keybinds.merge(other.keybinds);
}
}

impl<'de> Deserialize<'de> for Keybinds {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
Expand Down
26 changes: 26 additions & 0 deletions tools/twix/src/configuration/merge.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use std::collections::HashMap;
use std::hash::Hash;

pub trait Merge<T> {
/// Merge `other` into `self`.
/// `self` acts as a baseline,
/// and items in `other` take precedence in the case of a conflict.
fn merge(&mut self, other: T);
}

impl<K, V> Merge<Self> for HashMap<K, V>
where
K: Eq + Hash,
{
fn merge(&mut self, other: Self) {
self.extend(other);
}
}

impl<T: Merge<T>> Merge<Option<T>> for T {
fn merge(&mut self, other: Option<T>) {
if let Some(value) = other {
self.merge(value);
}
}
}

0 comments on commit 1559bc1

Please sign in to comment.