diff --git a/Cargo.lock b/Cargo.lock index dcf6c8a..fbbda1e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -314,15 +314,6 @@ dependencies = [ "powerfmt", ] -[[package]] -name = "derive_macro" -version = "0.1.0" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "derive_more" version = "2.0.1" @@ -438,6 +429,27 @@ dependencies = [ "once_cell", ] +[[package]] +name = "filecaster" +version = "0.2.3" +dependencies = [ + "filecaster-derive", + "merge", + "serde", +] + +[[package]] +name = "filecaster-derive" +version = "0.2.3" +dependencies = [ + "merge", + "proc-macro-error2", + "proc-macro2", + "quote", + "serde", + "syn", +] + [[package]] name = "fnv" version = "1.0.7" @@ -2045,9 +2057,9 @@ version = "0.1.0" dependencies = [ "color-eyre", "crossterm 0.29.0", - "derive_macro", "derive_more", "dirs", + "filecaster", "merge", "ratatui", "serde", diff --git a/Cargo.toml b/Cargo.toml index 4e85cf4..843408a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,9 +6,11 @@ license = "GPLv3" edition = "2024" [dependencies] +filecaster = { version = "0.2", features = [ + "merge", +], path = "../filecaster/filecaster/" } color-eyre = "0.6" crossterm = "0.29" -derive_macro = { path = "derive_macro" } derive_more = { version = "2.0", features = ["display"] } dirs = "6.0" merge = "0.2" diff --git a/derive_macro/Cargo.lock b/derive_macro/Cargo.lock deleted file mode 100644 index 3a90cca..0000000 --- a/derive_macro/Cargo.lock +++ /dev/null @@ -1,47 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "derive_macro" -version = "0.1.0" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "proc-macro2" -version = "1.0.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "syn" -version = "2.0.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "unicode-ident" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" diff --git a/derive_macro/Cargo.toml b/derive_macro/Cargo.toml deleted file mode 100644 index 4cbb5a8..0000000 --- a/derive_macro/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "derive_macro" -version = "0.1.0" -edition = "2024" - -[lib] -crate-type = ["proc-macro"] - -[dependencies] -proc-macro2 = "1.0" -quote = "1.0" -syn = { version = "2.0", features = ["full", "extra-traits"] } diff --git a/derive_macro/src/from_file.rs b/derive_macro/src/from_file.rs deleted file mode 100644 index d05c83c..0000000 --- a/derive_macro/src/from_file.rs +++ /dev/null @@ -1,42 +0,0 @@ -use proc_macro::TokenStream; -use quote::quote; -use syn::{Data, DeriveInput, Fields, Ident}; - -pub fn impl_from_file(input: DeriveInput) -> TokenStream { - let name = &input.ident; - let file_name = format!("{name}File"); - let file_ident = Ident::new(&file_name, name.span()); - - let fields = match &input.data { - Data::Struct(data) => match &data.fields { - Fields::Named(fields) => &fields.named, - _ => panic!("Only named fields are supported"), - }, - _ => panic!("Only structs are supported"), - }; - - let field_idents = fields.iter().map(|f| &f.ident); - let field_idents2 = field_idents.clone(); - - quote! { - impl #name { - fn from_file(file: Option<#file_ident>) -> Self - where - #file_ident: Default + Clone - { - let default = #file_ident::default(); - let file = file.unwrap_or_else(|| default.clone()); - - Self { - #(#field_idents: file.#field_idents2.unwrap_or_else(|| default.#field_idents.clone().unwrap())),* - } - } - } - - impl From> for #name { - fn from(value: Option<#file_ident>) -> Self { - Self::from_file(value) - } - } - }.into() -} diff --git a/derive_macro/src/lib.rs b/derive_macro/src/lib.rs deleted file mode 100644 index 7f91730..0000000 --- a/derive_macro/src/lib.rs +++ /dev/null @@ -1,10 +0,0 @@ -mod from_file; - -use proc_macro::TokenStream; -use syn::{DeriveInput, parse_macro_input}; - -#[proc_macro_derive(FromFile)] -pub fn derive_from_file(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as DeriveInput); - from_file::impl_from_file(input) -} diff --git a/src/config/color.rs b/src/config/color.rs index 86be3d1..e0b62fe 100644 --- a/src/config/color.rs +++ b/src/config/color.rs @@ -1,34 +1,13 @@ -use derive_macro::FromFile; -use merge::{Merge, option::overwrite_none}; -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Clone, Deserialize, Serialize, Merge)] -pub struct ColorConfigFile { - #[merge(strategy = overwrite_none)] - pub highlight_background: Option, - #[merge(strategy = overwrite_none)] - pub highlight_foreground: Option, - #[merge(strategy = overwrite_none)] - pub header_foreground: Option, - #[merge(strategy = overwrite_none)] - pub info_foreground: Option, -} +use filecaster::FromFile; #[derive(Debug, Clone, FromFile)] pub struct ColorConfig { + #[from_file(default = "magenta")] pub highlight_background: String, + #[from_file(default = "black")] pub highlight_foreground: String, + #[from_file(default = "yellow")] pub header_foreground: String, + #[from_file(default = "blue")] pub info_foreground: String, } - -impl Default for ColorConfigFile { - fn default() -> Self { - Self { - highlight_background: Some("magenta".to_string()), - highlight_foreground: Some("black".to_string()), - header_foreground: Some("yellow".to_string()), - info_foreground: Some("blue".to_string()), - } - } -} diff --git a/src/config/keybinds.rs b/src/config/keybinds.rs index d23685f..facb938 100644 --- a/src/config/keybinds.rs +++ b/src/config/keybinds.rs @@ -1,78 +1,35 @@ -use derive_macro::FromFile; -use merge::{Merge, option::overwrite_none}; -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Clone, Deserialize, Serialize, Merge)] -pub struct KeybindsConfigFile { - #[merge(strategy = overwrite_none)] - pub quit: Option, - #[merge(strategy = overwrite_none)] - pub next_tab: Option, - #[merge(strategy = overwrite_none)] - pub prev_tab: Option, - #[merge(strategy = overwrite_none)] - pub next_torrent: Option, - #[merge(strategy = overwrite_none)] - pub prev_torrent: Option, - #[merge(strategy = overwrite_none)] - pub switch_tab_1: Option, - #[merge(strategy = overwrite_none)] - pub switch_tab_2: Option, - #[merge(strategy = overwrite_none)] - pub switch_tab_3: Option, - #[merge(strategy = overwrite_none)] - pub toggle_torrent: Option, - #[merge(strategy = overwrite_none)] - pub toggle_all: Option, - #[merge(strategy = overwrite_none)] - pub delete: Option, - #[merge(strategy = overwrite_none)] - pub delete_force: Option, - #[merge(strategy = overwrite_none)] - pub select: Option, - #[merge(strategy = overwrite_none)] - pub toggle_help: Option, - #[merge(strategy = overwrite_none)] - pub move_torrent: Option, -} +use filecaster::FromFile; #[derive(Debug, Clone, FromFile)] pub struct KeybindsConfig { + #[from_file(default = "q")] pub quit: String, + #[from_file(default = "l")] pub next_tab: String, + #[from_file(default = "h")] pub prev_tab: String, + #[from_file(default = "j")] pub next_torrent: String, + #[from_file(default = "k")] pub prev_torrent: String, + #[from_file(default = "1")] pub switch_tab_1: String, + #[from_file(default = "2")] pub switch_tab_2: String, + #[from_file(default = "3")] pub switch_tab_3: String, + #[from_file(default = "enter")] pub toggle_torrent: String, + #[from_file(default = "a")] pub toggle_all: String, + #[from_file(default = "d")] pub delete: String, + #[from_file(default = "D")] pub delete_force: String, + #[from_file(default = " ")] pub select: String, + #[from_file(default = "?")] pub toggle_help: String, + #[from_file(default = "m")] pub move_torrent: String, } - -impl Default for KeybindsConfigFile { - fn default() -> Self { - Self { - quit: Some("q".to_string()), - next_tab: Some("l".to_string()), - prev_tab: Some("h".to_string()), - next_torrent: Some("j".to_string()), - prev_torrent: Some("k".to_string()), - switch_tab_1: Some("1".to_string()), - switch_tab_2: Some("2".to_string()), - switch_tab_3: Some("3".to_string()), - toggle_torrent: Some("enter".to_string()), - toggle_all: Some("a".to_string()), - delete: Some("d".to_string()), - delete_force: Some("D".to_string()), - select: Some(" ".to_string()), - toggle_help: Some("?".to_string()), - move_torrent: Some("m".to_string()), - } - } -} diff --git a/src/config/log.rs b/src/config/log.rs index 66b6e89..a7d8b60 100644 --- a/src/config/log.rs +++ b/src/config/log.rs @@ -1,30 +1,11 @@ -use derive_macro::FromFile; -use merge::{Merge, option::overwrite_none}; -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Clone, Deserialize, Serialize, Merge)] -pub struct LogConfigFile { - #[merge(strategy = overwrite_none)] - pub traxor: Option, - #[merge(strategy = overwrite_none)] - pub ratatui: Option, - #[merge(strategy = overwrite_none)] - pub transmission_rpc: Option, -} +use filecaster::FromFile; #[derive(Debug, Clone, FromFile)] pub struct LogConfig { + #[from_file(default = "warn")] pub traxor: String, + #[from_file(default = "warn")] pub ratatui: String, + #[from_file(default = "warn")] pub transmission_rpc: String, } - -impl Default for LogConfigFile { - fn default() -> Self { - Self { - traxor: Some("warn".to_string()), - ratatui: Some("warn".to_string()), - transmission_rpc: Some("warn".to_string()), - } - } -} diff --git a/src/config/mod.rs b/src/config/mod.rs index 9c8ff8d..7d63787 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -2,32 +2,22 @@ pub mod color; pub mod keybinds; pub mod log; -use color::{ColorConfig, ColorConfigFile}; +use color::ColorConfig; use color_eyre::{ Result, eyre::{Context, ContextCompat, Ok}, }; -use keybinds::{KeybindsConfig, KeybindsConfigFile}; -use log::{LogConfig, LogConfigFile}; -use merge::{Merge, option::overwrite_none}; -use serde::{Deserialize, Serialize}; +use filecaster::FromFile; +use keybinds::KeybindsConfig; +use log::LogConfig; +use merge::Merge; use std::{ fs::read_to_string, path::{Path, PathBuf}, }; use tracing::{debug, info, warn}; -#[derive(Debug, Clone, Default, Deserialize, Serialize, Merge)] -pub struct ConfigFile { - #[merge(strategy = overwrite_none)] - pub keybinds: Option, - #[merge(strategy = overwrite_none)] - pub colors: Option, - #[merge(strategy = overwrite_none)] - pub log: Option, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, FromFile)] pub struct Config { pub keybinds: KeybindsConfig, pub colors: ColorConfig, @@ -56,16 +46,6 @@ impl Config { } } -impl From for Config { - fn from(value: ConfigFile) -> Self { - Self { - keybinds: value.keybinds.into(), - colors: value.colors.into(), - log: value.log.into(), - } - } -} - #[tracing::instrument(name = "Getting config path")] fn get_config_path() -> Result { let config_dir = diff --git a/tests/handler.rs b/tests/handler.rs index 299c9f7..77a638e 100644 --- a/tests/handler.rs +++ b/tests/handler.rs @@ -120,3 +120,34 @@ async fn test_get_action_unhandled() { None ); } + +#[tokio::test] +async fn test_get_action_toggle_help() { + let config = Config::load().unwrap(); + let mut app = App::new(config).unwrap(); + assert_eq!( + get_action(KeyEvent::from(KeyCode::Char('?')), &mut app) + .await + .unwrap(), + Some(Action::ToggleHelp) + ); +} + +#[tokio::test] +async fn test_get_action_input_mode() { + let config = Config::load().unwrap(); + let mut app = App::new(config).unwrap(); + app.input_mode = true; + assert_eq!( + get_action(KeyEvent::from(KeyCode::Enter), &mut app) + .await + .unwrap(), + Some(Action::Submit) + ); + assert_eq!( + get_action(KeyEvent::from(KeyCode::Esc), &mut app) + .await + .unwrap(), + Some(Action::Cancel) + ); +}