moving to config.toml

This commit is contained in:
filipriec
2025-02-17 20:09:53 +01:00
parent c150296274
commit c5dee41757
7 changed files with 215 additions and 65 deletions

53
Cargo.lock generated
View File

@@ -1374,6 +1374,7 @@ dependencies = [
"serde_with", "serde_with",
"sqlx", "sqlx",
"tokio", "tokio",
"toml",
"tonic", "tonic",
"tonic-build", "tonic-build",
"tracing", "tracing",
@@ -1955,6 +1956,15 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "serde_spanned"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "serde_urlencoded" name = "serde_urlencoded"
version = "0.7.1" version = "0.7.1"
@@ -2577,6 +2587,40 @@ dependencies = [
"tokio", "tokio",
] ]
[[package]]
name = "toml"
version = "0.8.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit",
]
[[package]]
name = "toml_datetime"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.22.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474"
dependencies = [
"indexmap 2.7.1",
"serde",
"serde_spanned",
"toml_datetime",
"winnow",
]
[[package]] [[package]]
name = "tonic" name = "tonic"
version = "0.12.3" version = "0.12.3"
@@ -3117,6 +3161,15 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winnow"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59690dea168f2198d1a3b0cac23b8063efcd11012f10ae4698f284808c8ef603"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "wit-bindgen-rt" name = "wit-bindgen-rt"
version = "0.33.0" version = "0.33.0"

View File

@@ -16,6 +16,7 @@ serde_json = "1.0.138"
serde_with = "3.12.0" serde_with = "3.12.0"
sqlx = { version = "0.8.3", features = ["postgres", "runtime-tokio", "runtime-tokio-native-tls", "time"] } sqlx = { version = "0.8.3", features = ["postgres", "runtime-tokio", "runtime-tokio-native-tls", "time"] }
tokio = { version = "1.43.0", features = ["full", "macros"] } tokio = { version = "1.43.0", features = ["full", "macros"] }
toml = "0.8.20"
tonic = "0.12.3" tonic = "0.12.3"
tonic-build = "0.12.3" tonic-build = "0.12.3"
tracing = "0.1.41" tracing = "0.1.41"

6
config.toml Normal file
View File

@@ -0,0 +1,6 @@
# config.toml
[keybindings]
save = [":w", "ctrl+s"]
quit = [":q", "ctrl+q"]
force_quit = [":q!", "ctrl+shift+q"]
save_and_quit = [":wq", "ctrl+shift+s"]

53
src/client/config.rs Normal file
View File

@@ -0,0 +1,53 @@
// src/client/config.rs
use serde::Deserialize;
use std::collections::HashMap;
use crossterm::event::{KeyCode, KeyModifiers};
#[derive(Debug, Deserialize)]
pub struct Config {
pub keybindings: HashMap<String, Vec<String>>,
}
impl Config {
pub fn load() -> Result<Self, Box<dyn std::error::Error>> {
let config_str = std::fs::read_to_string("config.toml")?;
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 {
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,
_ => {
expected_key = match part.to_lowercase().as_str() {
"s" => Some(KeyCode::Char('s')),
"q" => Some(KeyCode::Char('q')),
"w" => Some(KeyCode::Char('w')),
":" => Some(KeyCode::Char(':')),
_ => None,
};
}
}
}
modifiers == expected_modifiers && Some(key) == expected_key
}
}

View File

@@ -3,5 +3,7 @@ mod ui;
mod colors; mod colors;
mod components; mod components;
mod terminal; mod terminal;
mod config;
pub use ui::run_ui; pub use ui::run_ui;
pub use config::Config;

View File

@@ -1,5 +1,5 @@
// src/client/terminal.rs // src/client/terminal.rs
use crossterm::event::{self, Event}; use crossterm::event::{self, Event, KeyCode, KeyModifiers};
use crossterm::{ use crossterm::{
execute, execute,
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
@@ -8,6 +8,7 @@ use ratatui::{backend::CrosstermBackend, Terminal};
use std::io::{self, stdout}; use std::io::{self, stdout};
use tonic::transport::Channel; use tonic::transport::Channel;
use crate::proto::multieko2::adresar_client::AdresarClient; use crate::proto::multieko2::adresar_client::AdresarClient;
use crate::client::config::Config;
use crate::proto::multieko2::AdresarRequest; use crate::proto::multieko2::AdresarRequest;
pub struct AppTerminal { pub struct AppTerminal {
@@ -49,12 +50,12 @@ impl AppTerminal {
pub async fn handle_command( pub async fn handle_command(
&mut self, &mut self,
command_input: &str, action: &str,
is_saved: &mut bool, is_saved: &mut bool,
form_data: &AdresarRequest, // Pass form data here form_data: &AdresarRequest,
) -> Result<(bool, String), Box<dyn std::error::Error>> { ) -> Result<(bool, String), Box<dyn std::error::Error>> {
match command_input { match action {
"w" => { "save" => {
// Send data to the server // Send data to the server
let request = tonic::Request::new(form_data.clone()); let request = tonic::Request::new(form_data.clone());
let response = self.grpc_client.create_adresar(request).await?; let response = self.grpc_client.create_adresar(request).await?;
@@ -62,7 +63,7 @@ impl AppTerminal {
*is_saved = true; *is_saved = true;
Ok((false, format!("State saved. Response: {:?}", response))) Ok((false, format!("State saved. Response: {:?}", response)))
} }
"q" => { "quit" => {
if *is_saved { if *is_saved {
self.cleanup()?; self.cleanup()?;
Ok((true, "Exiting.".to_string())) Ok((true, "Exiting.".to_string()))
@@ -70,16 +71,16 @@ impl AppTerminal {
Ok((false, "No changes saved. Use :q! to force quit.".to_string())) Ok((false, "No changes saved. Use :q! to force quit.".to_string()))
} }
} }
"q!" => { "force_quit" => {
self.cleanup()?; self.cleanup()?;
Ok((true, "Force exiting without saving.".to_string())) Ok((true, "Force exiting without saving.".to_string()))
} }
"wq" => { "save_and_quit" => {
*is_saved = true; *is_saved = true;
self.cleanup()?; self.cleanup()?;
Ok((true, "State saved. Exiting.".to_string())) Ok((true, "State saved. Exiting.".to_string()))
} }
_ => Ok((false, format!("Command not recognized: {}", command_input))), _ => Ok((false, format!("Action not recognized: {}", action))),
} }
} }
} }

View File

@@ -3,11 +3,13 @@ use crossterm::event::{Event, KeyCode, KeyModifiers};
use crate::client::terminal::AppTerminal; use crate::client::terminal::AppTerminal;
use crate::client::components::{render_command_line, render_form, render_preview_card, render_status_line}; use crate::client::components::{render_command_line, render_form, render_preview_card, render_status_line};
use crate::client::colors::Theme; use crate::client::colors::Theme;
use crate::client::config::Config;
use ratatui::layout::{Constraint, Direction, Layout, Rect}; use ratatui::layout::{Constraint, Direction, Layout, Rect};
use std::env; use std::env;
use crate::proto::multieko2::AdresarRequest; use crate::proto::multieko2::AdresarRequest;
pub async fn run_ui() -> Result<(), Box<dyn std::error::Error>> { pub async fn run_ui() -> Result<(), Box<dyn std::error::Error>> {
let config = Config::load()?;
let mut app_terminal = AppTerminal::new().await?; let mut app_terminal = AppTerminal::new().await?;
let mut command_mode = false; let mut command_mode = false;
let mut command_input = String::new(); let mut command_input = String::new();
@@ -118,8 +120,10 @@ pub async fn run_ui() -> Result<(), Box<dyn std::error::Error>> {
fax: fax.clone(), fax: fax.clone(),
}; };
// Pass form data to handle_command // Pass form data to handle_command (remove &config)
let (should_exit, message) = app_terminal.handle_command(&command_input, &mut is_saved, &form_data).await?; let (should_exit, message) = app_terminal
.handle_command(&command_input, &mut is_saved, &form_data)
.await?;
command_message = message; command_message = message;
if should_exit { if should_exit {
return Ok(()); return Ok(());
@@ -138,6 +142,35 @@ pub async fn run_ui() -> Result<(), Box<dyn std::error::Error>> {
} }
_ => {} _ => {}
} }
} else {
// Check for keybindings
if let Some(action) = config.get_action_for_key(key.code, key.modifiers) {
let form_data = AdresarRequest {
firma: firma.clone(),
kz: kz.clone(),
drc: drc.clone(),
ulica: ulica.clone(),
psc: psc.clone(),
mesto: mesto.clone(),
stat: stat.clone(),
banka: banka.clone(),
ucet: ucet.clone(),
skladm: skladm.clone(),
ico: ico.clone(),
kontakt: kontakt.clone(),
telefon: telefon.clone(),
skladu: skladu.clone(),
fax: fax.clone(),
};
// Pass form data to handle_command (remove &config)
let (should_exit, message) = app_terminal
.handle_command(action, &mut is_saved, &form_data)
.await?;
command_message = message;
if should_exit {
return Ok(());
}
} else { } else {
match key.code { match key.code {
KeyCode::Char(':') => { KeyCode::Char(':') => {
@@ -201,4 +234,5 @@ pub async fn run_ui() -> Result<(), Box<dyn std::error::Error>> {
} }
} }
} }
}
} }