diff --git a/Cargo.lock b/Cargo.lock index 2611521..4c8bac8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -143,7 +143,7 @@ checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -321,7 +321,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -346,6 +346,15 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "colored" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "compact_str" version = "0.8.1" @@ -457,7 +466,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -468,7 +477,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -500,7 +509,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -577,7 +586,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -733,7 +742,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -1122,7 +1131,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -1191,7 +1200,7 @@ dependencies = [ "indoc", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -1215,6 +1224,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.14" @@ -1340,7 +1358,7 @@ dependencies = [ "chrono", "clap", "clap_complete", - "colored", + "colored 3.0.0", "confy", "crossterm", "ctrlc", @@ -1351,7 +1369,7 @@ dependencies = [ "human-panic", "indicatif", "indoc", - "itertools", + "itertools 0.14.0", "log", "log4rs", "managarr-tree-widget", @@ -1373,6 +1391,7 @@ dependencies = [ "tokio", "tokio-util", "urlencoding", + "validate_theme_derive", "veil", ] @@ -1442,7 +1461,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -1453,7 +1472,7 @@ checksum = "652cd6d169a36eaf9d1e6bce1a221130439a966d7f27858af66a33a66e9c4ee2" dependencies = [ "assert-json-diff", "bytes", - "colored", + "colored 2.2.0", "futures-util", "http", "http-body", @@ -1566,7 +1585,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -1754,9 +1773,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.38" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +checksum = "c1f1914ce909e1658d9907913b4b91947430c7d9be598b15a1912935b8c04801" dependencies = [ "proc-macro2", ] @@ -1803,7 +1822,7 @@ dependencies = [ "crossterm", "indoc", "instability", - "itertools", + "itertools 0.13.0", "lru", "paste", "strum", @@ -1928,21 +1947,21 @@ dependencies = [ [[package]] name = "rstest" -version = "0.23.0" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a2c585be59b6b5dd66a9d2084aa1d8bd52fbdb806eafdeffb52791147862035" +checksum = "6fc39292f8613e913f7df8fa892b8944ceb47c247b78e1b1ae2f09e019be789d" dependencies = [ - "futures", "futures-timer", + "futures-util", "rstest_macros", "rustc_version", ] [[package]] name = "rstest_macros" -version = "0.23.0" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "825ea780781b15345a146be27eaefb05085e337e869bff01b4306a4fd4a9ad5a" +checksum = "1f168d99749d307be9de54d23fd226628d99768225ef08f6ffb52e0182a27746" dependencies = [ "cfg-if", "glob", @@ -1952,7 +1971,7 @@ dependencies = [ "regex", "relative-path", "rustc_version", - "syn 2.0.98", + "syn 2.0.99", "unicode-ident", ] @@ -2121,7 +2140,7 @@ checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -2192,7 +2211,7 @@ checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -2299,7 +2318,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -2321,9 +2340,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.98" +version = "2.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" +checksum = "e02e925281e18ffd9d640e234264753c43edc62d64b2d4cf898f1bc5e75f3fc2" dependencies = [ "proc-macro2", "quote", @@ -2347,7 +2366,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -2418,7 +2437,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -2488,7 +2507,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -2637,7 +2656,7 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" dependencies = [ - "itertools", + "itertools 0.13.0", "unicode-segmentation", "unicode-width 0.1.14", ] @@ -2719,6 +2738,14 @@ dependencies = [ "getrandom 0.3.1", ] +[[package]] +name = "validate_theme_derive" +version = "0.1.0" +dependencies = [ + "quote", + "syn 2.0.99", +] + [[package]] name = "vcpkg" version = "0.2.15" @@ -2743,7 +2770,7 @@ checksum = "5b2d5567b6fbd34e8f0488d56b648e67c0d999535f4af2060d14f9074b43e833" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -2801,7 +2828,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", "wasm-bindgen-shared", ] @@ -2836,7 +2863,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3141,7 +3168,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", "synstructure", ] @@ -3163,7 +3190,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] [[package]] @@ -3183,7 +3210,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", "synstructure", ] @@ -3212,5 +3239,5 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.99", ] diff --git a/Cargo.toml b/Cargo.toml index 06a0153..a2ca6e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,9 @@ license = "MIT" rust-version = "1.82.0" exclude = [".github", "CONTRIBUTING.md", "*.log", "tags"] +[workspace] +members = ["proc_macros/validate_theme_derive"] + [dependencies] anyhow = "1.0.68" backtrace = "0.3.74" @@ -41,11 +44,16 @@ ratatui = { version = "0.29.0", features = [ "unstable-widget-ref", ] } urlencoding = "2.1.2" -clap = { version = "4.5.20", features = ["derive", "cargo", "env", "wrap_help"] } +clap = { version = "4.5.20", features = [ + "derive", + "cargo", + "env", + "wrap_help", +] } clap_complete = "4.5.33" -itertools = "0.13.0" +itertools = "0.14.0" ctrlc = "3.4.5" -colored = "2.1.0" +colored = "3.0.0" async-trait = "0.1.83" dirs-next = "2.0.0" managarr-tree-widget = "0.24.0" @@ -55,13 +63,14 @@ deunicode = "1.6.0" paste = "1.0.15" openssl = { version = "0.10.70", features = ["vendored"] } veil = "0.2.0" +validate_theme_derive = { path = "proc_macros/validate_theme_derive" } [dev-dependencies] assert_cmd = "2.0.16" mockall = "0.13.0" mockito = "1.0.0" pretty_assertions = "1.3.0" -rstest = "0.23.0" +rstest = "0.25.0" serial_test = "3.2.0" [dev-dependencies.cargo-husky] diff --git a/proc_macros/validate_theme_derive/Cargo.toml b/proc_macros/validate_theme_derive/Cargo.toml new file mode 100644 index 0000000..8c55170 --- /dev/null +++ b/proc_macros/validate_theme_derive/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "validate_theme_derive" +version = "0.1.0" +edition = "2024" + +[lib] +proc-macro = true + +[dependencies] +quote = "1.0.39" +syn = "2.0.99" diff --git a/proc_macros/validate_theme_derive/src/lib.rs b/proc_macros/validate_theme_derive/src/lib.rs new file mode 100644 index 0000000..d82648b --- /dev/null +++ b/proc_macros/validate_theme_derive/src/lib.rs @@ -0,0 +1,41 @@ +use proc_macro::TokenStream; +use quote::quote; +use syn::{parse_macro_input, Data, DeriveInput, Fields}; + +#[proc_macro_derive(ValidateTheme, attributes(validate))] +pub fn derive_validate_theme(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + let struct_name = &input.ident; + + let mut validation_checks = Vec::new(); + + if let Data::Struct(data_struct) = &input.data { + if let Fields::Named(fields) = &data_struct.fields { + for field in &fields.named { + let field_name = &field.ident; + + let has_validate_attr = field.attrs.iter().any(|attr| { + attr.path().is_ident("validate") + }); + + if has_validate_attr { + validation_checks.push(quote! { + if self.#field_name.is_none() { + log::error!("{} is missing a color value.", stringify!(#field_name)); + eprintln!("{} is missing a color value.", stringify!(#field_name)); + process::exit(1); + } + }) + } + } + } + } + + quote! { + impl #struct_name { + pub fn validate(&self) { + #(#validation_checks)* + } + } + }.into() +} diff --git a/src/main.rs b/src/main.rs index 2915522..0bbe5f2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -222,6 +222,7 @@ async fn start_ui( Theme::default() }; debug!("Managarr loaded using theme: {theme:?}"); + theme.validate(); THEME.set(theme); let mut stdout = io::stdout(); diff --git a/src/ui/theme.rs b/src/ui/theme.rs index ad2ff5d..b8805e2 100644 --- a/src/ui/theme.rs +++ b/src/ui/theme.rs @@ -1,8 +1,10 @@ +use crate::process; use anyhow::Result; use derivative::Derivative; use ratatui::style::Color; use serde::{Deserialize, Serialize}; use std::str::FromStr; +use validate_theme_derive::ValidateTheme; #[cfg(test)] #[path = "theme_tests.rs"] @@ -34,7 +36,7 @@ pub struct Style { pub color: Option, } -#[derive(Debug, Deserialize, Serialize, Clone, Copy, Derivative)] +#[derive(Debug, Deserialize, Serialize, Clone, Copy, Derivative, ValidateTheme)] #[derivative(Default)] #[cfg_attr(test, derive(PartialEq, Eq))] pub struct Theme { @@ -43,51 +45,67 @@ pub struct Theme { value = "Some(Background { color: Some(Color::Rgb(35, 50, 55)), enabled: Some(true) })" ))] pub background: Option, + #[validate] #[serde(default = "default_awaiting_import_style")] #[derivative(Default(value = "Some(Style { color: Some(Color::Rgb(255, 170, 66)) })"))] pub awaiting_import: Option