use std::env;
use std::fs::OpenOptions;
use std::io::Read;
use std::path::PathBuf;
use crokey::{key, KeyCombination};
use lazy_static::lazy_static;
use nestify::nest;
use parking_lot::RwLock;
use ratatui::text::Span;
use serde::Deserialize;
use crate::app::app::App;
use crate::panic_error;
#[derive(Default, Copy, Clone, Deserialize)]
pub struct KeyBindingsConfig {
pub keybindings: KeyBindings,
}
nest! {
#[derive(Copy, Clone, Deserialize)]
pub struct KeyBindings {
pub main_menu: #[derive(Copy, Clone, Deserialize)] pub struct MainMenu {
pub exit: KeyCombination,
pub expand_collection: KeyCombination,
pub unselect_request: KeyCombination,
pub move_request_up: KeyCombination,
pub move_request_down: KeyCombination,
pub next_environment: KeyCombination,
pub display_cookies: KeyCombination,},
pub generic: #[derive(Copy, Clone, Deserialize)] pub struct Generic {
pub display_help: KeyCombination,
pub text_inputs: #[derive(Copy, Clone, Deserialize)] pub struct TexInputs {
pub text_input: #[derive(Copy, Clone, Deserialize)] pub struct TextInput {
pub cancel: KeyCombination,
pub validate: KeyCombination,
pub delete_backward: KeyCombination,
pub delete_forward: KeyCombination,
pub move_cursor_left: KeyCombination,
pub move_cursor_right: KeyCombination,
},
pub text_area_mode: #[derive(Copy, Clone, PartialEq, Deserialize)] pub enum TextAreaMode {
VimEmulation,
Custom(CustomTextArea)
}
},
pub navigation: #[derive(Copy, Clone, Deserialize)] pub struct Navigation {
pub move_cursor_up: KeyCombination,
pub move_cursor_down: KeyCombination,
pub move_cursor_left: KeyCombination,
pub move_cursor_right: KeyCombination,
pub alt_move_cursor_up: KeyCombination,
pub alt_move_cursor_down: KeyCombination,
pub alt_move_cursor_left: KeyCombination,
pub alt_move_cursor_right: KeyCombination,
pub go_back: KeyCombination,
pub select: KeyCombination,
},
pub list_and_table_actions: #[derive(Copy, Clone, Deserialize)] pub struct ListAndTableActions {
pub create_element: KeyCombination,
pub delete_element: KeyCombination,
pub edit_element: KeyCombination,
pub rename_element: KeyCombination,
pub toggle_element: KeyCombination,
}
},
pub request_selected: #[derive(Copy, Clone, Deserialize)] pub struct RequestSelected {
pub param_next_tab: KeyCombination,
pub change_url: KeyCombination,
pub change_method: KeyCombination,
pub request_settings: KeyCombination,
pub next_view: KeyCombination,
pub send_request: KeyCombination,
pub alt_send_request: KeyCombination,
pub param_tabs: #[derive(Copy, Clone, Deserialize)] pub struct ParamTabs {
pub change_auth_method: KeyCombination,
pub change_body_content_type: KeyCombination,
},
pub result_tabs: #[derive(Copy, Clone, Deserialize)] pub struct ResultTabs {
pub scroll_up: KeyCombination,
pub scroll_down: KeyCombination,
pub scroll_left: KeyCombination,
pub scroll_right: KeyCombination,
pub yank_response_part: KeyCombination,
pub result_next_tab: KeyCombination,
}
},
}
}
lazy_static! {
pub static ref KEY_BINDINGS: RwLock<KeyBindings> = RwLock::new(KeyBindings::default());
}
#[derive(Copy, Clone, PartialEq, Deserialize)]
pub struct CustomTextArea {
pub quit_without_saving: KeyCombination,
pub copy: KeyCombination,
pub paste: KeyCombination,
pub undo: KeyCombination,
pub redo: KeyCombination,
pub save_and_quit: KeyCombination,
pub new_line: KeyCombination,
pub indent: KeyCombination,
pub delete_backward: KeyCombination,
pub delete_forward: KeyCombination,
pub skip_word_right: KeyCombination,
pub skip_word_left: KeyCombination,
pub move_cursor_up: KeyCombination,
pub move_cursor_down: KeyCombination,
pub move_cursor_left: KeyCombination,
pub move_cursor_right: KeyCombination,
}
impl Default for KeyBindings {
fn default() -> Self {
KeyBindings {
main_menu: MainMenu {
exit: key!(q),
expand_collection: key!(right),
unselect_request: key!(left),
move_request_up: key!(ctrl-up),
move_request_down: key!(ctrl-down),
next_environment: key!(e),
display_cookies: key!(c),
},
generic: Generic {
display_help: key!(Ctrl-h),
text_inputs: TexInputs {
text_input: TextInput {
cancel: key!(esc),
validate: key!(enter),
delete_backward: key!(delete),
delete_forward: key!(backspace),
move_cursor_left: key!(left),
move_cursor_right: key!(right),
},
text_area_mode: TextAreaMode::Custom(CustomTextArea::default()),
},
navigation: Navigation {
move_cursor_up: key!(up),
move_cursor_down: key!(down),
move_cursor_left: key!(left),
move_cursor_right: key!(right),
alt_move_cursor_up: key!(Up),
alt_move_cursor_down: key!(Down),
alt_move_cursor_left: key!(Left),
alt_move_cursor_right: key!(Right),
go_back: key!(esc),
select: key!(enter),
},
list_and_table_actions: ListAndTableActions {
create_element: key!(n),
delete_element: key!(d),
edit_element: key!(enter),
rename_element: key!(r),
toggle_element: key!(t),
},
},
request_selected: RequestSelected {
param_next_tab: key!(tab),
change_url: key!(u),
change_method: key!(m),
request_settings: key!(s),
next_view: key!(v),
send_request: key!(space),
alt_send_request: key!(ctrl-enter),
param_tabs: ParamTabs {
change_auth_method: key!(ctrl-a),
change_body_content_type: key!(ctrl-b),
},
result_tabs: ResultTabs {
scroll_up: key!(ctrl-up),
scroll_down: key!(ctrl-down),
scroll_left: key!(ctrl-left),
scroll_right: key!(ctrl-right),
yank_response_part: key!(y),
result_next_tab: key!(shift-backtab),
},
}
}
}
}
impl Default for CustomTextArea {
fn default() -> Self {
CustomTextArea {
quit_without_saving: key!(esc),
copy: key!(ctrl-c),
paste: key!(ctrl-v),
undo: key!(ctrl-z),
redo: key!(ctrl-y),
save_and_quit: key!(ctrl-s),
new_line: key!(enter),
indent: key!(tab),
delete_backward: key!(delete),
delete_forward: key!(backspace),
skip_word_right: key!(ctrl-right),
skip_word_left: key!(ctrl-left),
move_cursor_up: key!(up),
move_cursor_down: key!(down),
move_cursor_left: key!(left),
move_cursor_right: key!(right),
}
}
}
impl App<'_> {
pub fn parse_key_bindings_file(&mut self) {
let path = match env::var("ATAC_KEY_BINDINGS") {
Ok(env_key_bindings) => PathBuf::from(env_key_bindings),
Err(_) => {
println!("No key bindings file found\n");
return;
}
};
println!("Parsing key bindings file: {}", path.display());
let mut key_bindings_file = match OpenOptions::new().read(true).open(path) {
Ok(key_bindings_file) => key_bindings_file,
Err(e) => panic_error(format!("Could not open key bindings file\n\t{e}"))
};
let mut file_content = String::new();
key_bindings_file.read_to_string(&mut file_content).expect("\tCould not read key bindings file");
let config: KeyBindingsConfig = match toml::from_str(&file_content) {
Ok(config) => config,
Err(e) => panic_error(format!("Could not parse key bindings file\n\t{e}"))
};
*KEY_BINDINGS.write() = config.keybindings;
println!("Key bindings file parsed!\n");
}
}
pub fn unique_key_and_help(help: Span<'static>, key: Span<'static>) -> Vec<Span<'static>> {
if help.to_string() == key.to_string() {
return vec![help];
}
else {
vec![help, Span::raw(" "), key]
}
}