project structure redesign
This commit is contained in:
134
client/src/config/config.rs
Normal file
134
client/src/config/config.rs
Normal file
@@ -0,0 +1,134 @@
|
||||
// client/src/config.rs
|
||||
|
||||
use serde::Deserialize;
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use crossterm::event::{KeyCode, KeyModifiers};
|
||||
|
||||
#[derive(Debug, Deserialize, Default)]
|
||||
pub struct ColorsConfig {
|
||||
#[serde(default = "default_theme")]
|
||||
pub theme: String,
|
||||
}
|
||||
|
||||
fn default_theme() -> String {
|
||||
"light".to_string()
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Config {
|
||||
pub keybindings: HashMap<String, Vec<String>>,
|
||||
#[serde(default)]
|
||||
pub colors: ColorsConfig,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
/// Loads the configuration from "config.toml" in the client crate directory.
|
||||
pub fn load() -> Result<Self, Box<dyn std::error::Error>> {
|
||||
// env!("CARGO_MANIFEST_DIR") resolves to the directory where the Cargo.toml of
|
||||
// the client crate is located.
|
||||
let manifest_dir = env!("CARGO_MANIFEST_DIR");
|
||||
let config_path = Path::new(manifest_dir).join("config.toml");
|
||||
let config_str = std::fs::read_to_string(&config_path)
|
||||
.map_err(|e| format!("Failed to read config file at {:?}: {}", config_path, e))?;
|
||||
let config: Config = toml::from_str(&config_str)?;
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
pub fn get_action_for_key(
|
||||
&self,
|
||||
key: KeyCode,
|
||||
modifiers: KeyModifiers,
|
||||
) -> Option<&str> {
|
||||
for (action, bindings) in &self.keybindings {
|
||||
// Skip mode toggle actions
|
||||
if action == "enter_edit_mode" || action == "exit_edit_mode" {
|
||||
continue;
|
||||
}
|
||||
for binding in bindings {
|
||||
if Self::matches_keybinding(binding, key, modifiers) {
|
||||
return Some(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn matches_keybinding(
|
||||
binding: &str,
|
||||
key: KeyCode,
|
||||
modifiers: KeyModifiers,
|
||||
) -> bool {
|
||||
let parts: Vec<&str> = binding.split('+').collect();
|
||||
let mut expected_modifiers = KeyModifiers::empty();
|
||||
let mut expected_key = None;
|
||||
|
||||
for part in parts {
|
||||
match part.to_lowercase().as_str() {
|
||||
"ctrl" => expected_modifiers |= KeyModifiers::CONTROL,
|
||||
"shift" => expected_modifiers |= KeyModifiers::SHIFT,
|
||||
"alt" => expected_modifiers |= KeyModifiers::ALT,
|
||||
"esc" => expected_key = Some(KeyCode::Esc),
|
||||
":" => expected_key = Some(KeyCode::Char(':')),
|
||||
part if part.len() == 1 => {
|
||||
let c = part.chars().next().unwrap();
|
||||
expected_key = Some(KeyCode::Char(c));
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
modifiers == expected_modifiers && Some(key) == expected_key
|
||||
}
|
||||
|
||||
pub fn get_action_for_command(&self, command: &str) -> Option<&str> {
|
||||
for (action, bindings) in &self.keybindings {
|
||||
for binding in bindings {
|
||||
if binding.starts_with(':') && binding.trim_start_matches(':') == command {
|
||||
return Some(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn is_enter_edit_mode_before(&self, key: KeyCode, modifiers: KeyModifiers) -> bool {
|
||||
if let Some(bindings) = self.keybindings.get("enter_edit_mode_before") {
|
||||
bindings.iter().any(|b| Self::matches_keybinding(b, key, modifiers))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_enter_edit_mode_after(&self, key: KeyCode, modifiers: KeyModifiers) -> bool {
|
||||
if let Some(bindings) = self.keybindings.get("enter_edit_mode_after") {
|
||||
bindings.iter().any(|b| Self::matches_keybinding(b, key, modifiers))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_enter_edit_mode(&self, key: KeyCode, modifiers: KeyModifiers) -> bool {
|
||||
self.is_enter_edit_mode_before(key, modifiers) ||
|
||||
self.is_enter_edit_mode_after(key, modifiers)
|
||||
}
|
||||
|
||||
pub fn is_exit_edit_mode(&self, key: KeyCode, modifiers: KeyModifiers) -> bool {
|
||||
if let Some(bindings) = self.keybindings.get("exit_edit_mode") {
|
||||
bindings.iter().any(|b| Self::matches_keybinding(b, key, modifiers))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_key_for_action(&self, action: &str, key_char: char) -> bool {
|
||||
if let Some(bindings) = self.keybindings.get(action) {
|
||||
for binding in bindings {
|
||||
if binding == &key_char.to_string() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user