From 215e88ae0fee41d090b21ceb8bb1f44387babef7 Mon Sep 17 00:00:00 2001 From: Sergio <77530549+sergi0g@users.noreply.github.com> Date: Thu, 5 Dec 2024 20:22:26 +0200 Subject: [PATCH] Switch to serde for config parsing --- Cargo.lock | 22 ++++++----- Cargo.toml | 2 + src/config.rs | 106 ++++++++++---------------------------------------- 3 files changed, 35 insertions(+), 95 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6ab19fb..949d11e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -357,6 +357,8 @@ dependencies = [ "reqwest-middleware", "reqwest-retry", "rustc-hash", + "serde", + "serde_json", "termsize", "tokio", "xitca-web", @@ -1172,9 +1174,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -1473,18 +1475,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.210" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", @@ -1493,9 +1495,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ "itoa", "memchr", @@ -1623,9 +1625,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.68" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 6225e8a..107c3cd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,8 @@ reqwest-middleware = "0.3.3" rustc-hash = "2.0.0" http-link = "1.0.1" itertools = "0.13.0" +serde_json = "1.0.133" +serde = "1.0.215" [features] default = ["server", "cli"] diff --git a/src/config.rs b/src/config.rs index 2717553..e3f56b4 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,23 +1,35 @@ use std::path::PathBuf; use rustc_hash::FxHashMap; +use serde::Deserialize; use crate::error; -const VALID_KEYS: [&str; 4] = ["authentication", "theme", "insecure_registries", "socket"]; - -#[derive(Clone)] +#[derive(Clone, Deserialize)] pub enum Theme { + #[serde(rename = "default")] Default, + #[serde(rename = "blue")] Blue, } -#[derive(Clone)] +impl Default for Theme { + fn default() -> Self { + Self::Default + } +} + +#[derive(Clone, Deserialize)] +#[serde(deny_unknown_fields)] pub struct Config { + #[serde(default = "FxHashMap::default")] pub authentication: FxHashMap, + #[serde(default = "Theme::default")] pub theme: Theme, + #[serde(default = "Vec::default")] pub insecure_registries: Vec, pub socket: Option, + #[serde(skip_deserializing)] pub debug: bool, } @@ -37,7 +49,7 @@ impl Config { pub fn load(&self, path: Option) -> Self { let raw_config = match &path { Some(path) => std::fs::read_to_string(path), - None => Ok(String::from("{}")), // Empty config + None => return Self::new(), // Empty config }; if raw_config.is_err() { error!( @@ -47,87 +59,11 @@ impl Config { }; self.parse(&raw_config.unwrap()) // We can safely unwrap here } - /// Parses and validates the config. The process is quite manual and I would rather use a library, but I don't want to grow the dependency tree, for a config as simple as this one. - /// Many of these checks are stupid, but we either validate the config properly, or we don't at all, so... this is the result. I _am not_ proud of this code. + /// Parses and validates the config. pub fn parse(&self, raw_config: &str) -> Self { - let json = match json::parse(raw_config) { - Ok(v) => v, - Err(e) => error!("Failed to parse config!\n{}", e), - }; - // In the code, raw_ means the JsonValue from the parsed config, before it's validated. - - // Authentication - let raw_authentication = &json["authentication"]; - if !raw_authentication.is_null() && !raw_authentication.is_object() { - error!("Config key `authentication` must be an object!"); - } - let mut authentication: FxHashMap = FxHashMap::default(); - raw_authentication.entries().for_each(|(registry, key)| { - if !key.is_string() { - error!("Config key `authentication.{}` must be a string!", registry); - } - authentication.insert(registry.to_string(), key.to_string()); - }); - - // Theme - let raw_theme = &json["theme"]; - if !raw_theme.is_null() && !raw_theme.is_string() { - error!("Config key `theme` must be a string!"); - } - let theme: Theme = { - if raw_theme.is_null() { - Theme::Default - } else { - match raw_theme.as_str().unwrap() { - "default" => Theme::Default, - "blue" => Theme::Blue, - _ => { - error!("Config key `theme` must be one of: `default`, `blue`!"); - } - } - } - }; - - // Insecure registries - let raw_insecure_registries = &json["insecure_registries"]; - if !raw_insecure_registries.is_null() && !raw_insecure_registries.is_array() { - error!("Config key `insecure_registries` must be an array!"); - } - let insecure_registries: Vec = raw_insecure_registries - .members() - .map(|registry| { - if !registry.is_string() { - error!("Config key `insecure_registries` must only consist of strings!"); - } else { - registry.as_str().unwrap().to_owned() - } - }) - .collect(); - - // Socket - let raw_socket = &json["socket"]; - if !raw_socket.is_null() && !raw_socket.is_string() { - error!("Config key `socket` must be a string!"); - } - let socket: Option = if raw_socket.is_null() { - None - } else { - Some(raw_socket.to_string()) - }; - - // Check for extra keys - json.entries().for_each(|(key, _)| { - if !VALID_KEYS.contains(&key) { - error!("Invalid key `{}`", key) - } - }); - - Self { - authentication, - theme, - insecure_registries, - socket, - debug: false, + match serde_json::from_str(raw_config) { + Ok(config) => config, + Err(e) => error!("Unexpected error occured while parsing config: {}", e), } } }