moving to config.toml
This commit is contained in:
53
Cargo.lock
generated
53
Cargo.lock
generated
@@ -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"
|
||||||
|
|||||||
@@ -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
6
config.toml
Normal 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
53
src/client/config.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
146
src/client/ui.rs
146
src/client/ui.rs
@@ -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(());
|
||||||
@@ -139,64 +143,94 @@ pub async fn run_ui() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match key.code {
|
// Check for keybindings
|
||||||
KeyCode::Char(':') => {
|
if let Some(action) = config.get_action_for_key(key.code, key.modifiers) {
|
||||||
command_mode = true;
|
let form_data = AdresarRequest {
|
||||||
command_input.clear();
|
firma: firma.clone(),
|
||||||
command_message.clear();
|
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(());
|
||||||
}
|
}
|
||||||
KeyCode::Tab => {
|
} else {
|
||||||
if key.modifiers.contains(KeyModifiers::SHIFT) {
|
match key.code {
|
||||||
current_field = current_field.saturating_sub(1);
|
KeyCode::Char(':') => {
|
||||||
} else {
|
command_mode = true;
|
||||||
current_field = (current_field + 1) % fields.len();
|
command_input.clear();
|
||||||
|
command_message.clear();
|
||||||
}
|
}
|
||||||
}
|
KeyCode::Tab => {
|
||||||
KeyCode::BackTab => current_field = current_field.saturating_sub(1),
|
if key.modifiers.contains(KeyModifiers::SHIFT) {
|
||||||
KeyCode::Down => current_field = (current_field + 1) % fields.len(),
|
current_field = current_field.saturating_sub(1);
|
||||||
KeyCode::Up => current_field = current_field.saturating_sub(1),
|
} else {
|
||||||
KeyCode::Enter => current_field = (current_field + 1) % fields.len(),
|
current_field = (current_field + 1) % fields.len();
|
||||||
KeyCode::Char(c) => {
|
}
|
||||||
match current_field {
|
|
||||||
0 => firma.push(c),
|
|
||||||
1 => kz.push(c),
|
|
||||||
2 => drc.push(c),
|
|
||||||
3 => ulica.push(c),
|
|
||||||
4 => psc.push(c),
|
|
||||||
5 => mesto.push(c),
|
|
||||||
6 => stat.push(c),
|
|
||||||
7 => banka.push(c),
|
|
||||||
8 => ucet.push(c),
|
|
||||||
9 => skladm.push(c),
|
|
||||||
10 => ico.push(c),
|
|
||||||
11 => kontakt.push(c),
|
|
||||||
12 => telefon.push(c),
|
|
||||||
13 => skladu.push(c),
|
|
||||||
14 => fax.push(c),
|
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
|
KeyCode::BackTab => current_field = current_field.saturating_sub(1),
|
||||||
|
KeyCode::Down => current_field = (current_field + 1) % fields.len(),
|
||||||
|
KeyCode::Up => current_field = current_field.saturating_sub(1),
|
||||||
|
KeyCode::Enter => current_field = (current_field + 1) % fields.len(),
|
||||||
|
KeyCode::Char(c) => {
|
||||||
|
match current_field {
|
||||||
|
0 => firma.push(c),
|
||||||
|
1 => kz.push(c),
|
||||||
|
2 => drc.push(c),
|
||||||
|
3 => ulica.push(c),
|
||||||
|
4 => psc.push(c),
|
||||||
|
5 => mesto.push(c),
|
||||||
|
6 => stat.push(c),
|
||||||
|
7 => banka.push(c),
|
||||||
|
8 => ucet.push(c),
|
||||||
|
9 => skladm.push(c),
|
||||||
|
10 => ico.push(c),
|
||||||
|
11 => kontakt.push(c),
|
||||||
|
12 => telefon.push(c),
|
||||||
|
13 => skladu.push(c),
|
||||||
|
14 => fax.push(c),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KeyCode::Backspace => {
|
||||||
|
match current_field {
|
||||||
|
0 => firma.pop(),
|
||||||
|
1 => kz.pop(),
|
||||||
|
2 => drc.pop(),
|
||||||
|
3 => ulica.pop(),
|
||||||
|
4 => psc.pop(),
|
||||||
|
5 => mesto.pop(),
|
||||||
|
6 => stat.pop(),
|
||||||
|
7 => banka.pop(),
|
||||||
|
8 => ucet.pop(),
|
||||||
|
9 => skladm.pop(),
|
||||||
|
10 => ico.pop(),
|
||||||
|
11 => kontakt.pop(),
|
||||||
|
12 => telefon.pop(),
|
||||||
|
13 => skladu.pop(),
|
||||||
|
14 => fax.pop(),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
KeyCode::Backspace => {
|
|
||||||
match current_field {
|
|
||||||
0 => firma.pop(),
|
|
||||||
1 => kz.pop(),
|
|
||||||
2 => drc.pop(),
|
|
||||||
3 => ulica.pop(),
|
|
||||||
4 => psc.pop(),
|
|
||||||
5 => mesto.pop(),
|
|
||||||
6 => stat.pop(),
|
|
||||||
7 => banka.pop(),
|
|
||||||
8 => ucet.pop(),
|
|
||||||
9 => skladm.pop(),
|
|
||||||
10 => ico.pop(),
|
|
||||||
11 => kontakt.pop(),
|
|
||||||
12 => telefon.pop(),
|
|
||||||
13 => skladu.pop(),
|
|
||||||
14 => fax.pop(),
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user