diff --git a/Cargo.lock b/Cargo.lock index 74ff5314..c29959f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -62,6 +62,7 @@ dependencies = [ "serde", "simplelog", "toml", + "toml_config", ] [[package]] @@ -80,6 +81,7 @@ dependencies = [ "privdrop", "rand", "serde", + "toml_config", ] [[package]] @@ -109,6 +111,7 @@ dependencies = [ "signal-hook", "slab", "smartstring", + "toml_config", ] [[package]] @@ -130,6 +133,7 @@ dependencies = [ "rand_distr", "rustls", "serde", + "toml_config", ] [[package]] @@ -179,6 +183,7 @@ dependencies = [ "slab", "socket2 0.4.2", "tinytemplate", + "toml_config", ] [[package]] @@ -196,6 +201,7 @@ dependencies = [ "rand", "rand_distr", "serde", + "toml_config", ] [[package]] @@ -215,6 +221,7 @@ dependencies = [ "rand_distr", "serde", "socket2 0.4.2", + "toml_config", ] [[package]] @@ -259,6 +266,7 @@ dependencies = [ "signal-hook", "slab", "socket2 0.4.2", + "toml_config", "tungstenite", ] @@ -284,6 +292,7 @@ dependencies = [ "rustls", "serde", "serde_json", + "toml_config", "tungstenite", ] diff --git a/aquatic_cli_helpers/Cargo.toml b/aquatic_cli_helpers/Cargo.toml index 60ee058a..9c21e779 100644 --- a/aquatic_cli_helpers/Cargo.toml +++ b/aquatic_cli_helpers/Cargo.toml @@ -12,3 +12,4 @@ anyhow = "1" serde = { version = "1", features = ["derive"] } simplelog = "0.11" toml = "0.5" +toml_config = "0.1.0" diff --git a/aquatic_cli_helpers/src/lib.rs b/aquatic_cli_helpers/src/lib.rs index db8818e9..c8e7d84c 100644 --- a/aquatic_cli_helpers/src/lib.rs +++ b/aquatic_cli_helpers/src/lib.rs @@ -4,8 +4,9 @@ use std::io::Read; use anyhow::Context; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use simplelog::{ColorChoice, ConfigBuilder, LevelFilter, TermLogger, TerminalMode}; +use toml_config::TomlConfig; -#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, TomlConfig, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub enum LogLevel { Off, @@ -22,7 +23,7 @@ impl Default for LogLevel { } } -pub trait Config: Default + Serialize + DeserializeOwned { +pub trait Config: Default + TomlConfig + DeserializeOwned { fn get_log_level(&self) -> Option { None } @@ -169,9 +170,9 @@ where fn default_config_as_toml() -> String where - T: Default + Serialize, + T: Default + TomlConfig, { - toml::to_string_pretty(&T::default()).expect("Could not serialize default config to toml") + ::default_to_string() } fn start_logger(log_level: LogLevel) -> ::anyhow::Result<()> { diff --git a/aquatic_common/Cargo.toml b/aquatic_common/Cargo.toml index 7a072096..a6137aa4 100644 --- a/aquatic_common/Cargo.toml +++ b/aquatic_common/Cargo.toml @@ -24,6 +24,7 @@ log = "0.4" privdrop = "0.5" rand = { version = "0.8", features = ["small_rng"] } serde = { version = "1", features = ["derive"] } +toml_config = "0.1.0" # cpu-pinning hwloc = { version = "0.5", optional = true } diff --git a/aquatic_common/src/access_list.rs b/aquatic_common/src/access_list.rs index f5a1076a..d2a4c785 100644 --- a/aquatic_common/src/access_list.rs +++ b/aquatic_common/src/access_list.rs @@ -7,8 +7,9 @@ use anyhow::Context; use arc_swap::{ArcSwap, Cache}; use hashbrown::HashSet; use serde::{Deserialize, Serialize}; +use toml_config::TomlConfig; -#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, TomlConfig, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub enum AccessListMode { /// Only serve torrents with info hash present in file @@ -25,7 +26,7 @@ impl AccessListMode { } } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, TomlConfig, Deserialize)] pub struct AccessListConfig { pub mode: AccessListMode, /// Path to access list file consisting of newline-separated hex-encoded info hashes. diff --git a/aquatic_common/src/cpu_pinning.rs b/aquatic_common/src/cpu_pinning.rs index a4065dfc..0dc3e611 100644 --- a/aquatic_common/src/cpu_pinning.rs +++ b/aquatic_common/src/cpu_pinning.rs @@ -1,7 +1,8 @@ use hwloc::{CpuSet, ObjectType, Topology, CPUBIND_THREAD}; use serde::{Deserialize, Serialize}; +use toml_config::TomlConfig; -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, TomlConfig, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub enum CpuPinningMode { Ascending, @@ -14,7 +15,7 @@ impl Default for CpuPinningMode { } } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, TomlConfig, Deserialize)] pub struct CpuPinningConfig { pub active: bool, pub mode: CpuPinningMode, diff --git a/aquatic_common/src/privileges.rs b/aquatic_common/src/privileges.rs index a898969d..2be5b821 100644 --- a/aquatic_common/src/privileges.rs +++ b/aquatic_common/src/privileges.rs @@ -7,9 +7,10 @@ use std::{ }; use privdrop::PrivDrop; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize}; +use toml_config::TomlConfig; -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, TomlConfig, Deserialize)] #[serde(default)] pub struct PrivilegeConfig { /// Chroot and switch user after binding to sockets diff --git a/aquatic_http/Cargo.toml b/aquatic_http/Cargo.toml index fd6a1a50..5d1df0b8 100644 --- a/aquatic_http/Cargo.toml +++ b/aquatic_http/Cargo.toml @@ -38,6 +38,7 @@ serde = { version = "1", features = ["derive"] } signal-hook = { version = "0.3" } slab = "0.4" smartstring = "0.2" +toml_config = "0.1.0" [dev-dependencies] quickcheck = "1" diff --git a/aquatic_http/src/config.rs b/aquatic_http/src/config.rs index b7c1d31b..bc21f8e9 100644 --- a/aquatic_http/src/config.rs +++ b/aquatic_http/src/config.rs @@ -1,11 +1,12 @@ use std::{net::SocketAddr, path::PathBuf}; use aquatic_common::{access_list::AccessListConfig, privileges::PrivilegeConfig}; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize}; +use toml_config::TomlConfig; use aquatic_cli_helpers::LogLevel; -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, TomlConfig, Deserialize)] #[serde(default)] pub struct Config { /// Socket workers receive requests from the socket, parse them and send @@ -31,7 +32,7 @@ impl aquatic_cli_helpers::Config for Config { } } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, TomlConfig, Deserialize)] #[serde(default)] pub struct NetworkConfig { /// Bind to this address @@ -42,7 +43,7 @@ pub struct NetworkConfig { pub keep_alive: bool, } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, TomlConfig, Deserialize)] #[serde(default)] pub struct ProtocolConfig { /// Maximum number of torrents to accept in scrape request @@ -53,7 +54,7 @@ pub struct ProtocolConfig { pub peer_announce_interval: usize, } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, TomlConfig, Deserialize)] #[serde(default)] pub struct CleaningConfig { /// Clean peers this often (seconds) diff --git a/aquatic_http_load_test/Cargo.toml b/aquatic_http_load_test/Cargo.toml index eb36e870..78991d15 100644 --- a/aquatic_http_load_test/Cargo.toml +++ b/aquatic_http_load_test/Cargo.toml @@ -26,6 +26,7 @@ rand = { version = "0.8", features = ["small_rng"] } rand_distr = "0.4" rustls = { version = "0.20", features = ["dangerous_configuration"] } serde = { version = "1", features = ["derive"] } +toml_config = "0.1.0" [dev-dependencies] quickcheck = "1" diff --git a/aquatic_http_load_test/src/config.rs b/aquatic_http_load_test/src/config.rs index 3352957f..4ebc22b7 100644 --- a/aquatic_http_load_test/src/config.rs +++ b/aquatic_http_load_test/src/config.rs @@ -1,9 +1,10 @@ use std::net::SocketAddr; use aquatic_cli_helpers::LogLevel; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize}; +use toml_config::TomlConfig; -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, TomlConfig, Deserialize)] #[serde(default)] pub struct Config { pub server_address: SocketAddr, @@ -28,7 +29,7 @@ impl aquatic_cli_helpers::Config for Config { } } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, TomlConfig, Deserialize)] #[serde(default)] pub struct TorrentConfig { pub number_of_torrents: usize, diff --git a/aquatic_udp/Cargo.toml b/aquatic_udp/Cargo.toml index 84a65ada..1059b41f 100644 --- a/aquatic_udp/Cargo.toml +++ b/aquatic_udp/Cargo.toml @@ -36,6 +36,7 @@ slab = "0.4" signal-hook = { version = "0.3" } socket2 = { version = "0.4", features = ["all"] } tinytemplate = "1" +toml_config = "0.1.0" [dev-dependencies] quickcheck = "1" diff --git a/aquatic_udp/src/config.rs b/aquatic_udp/src/config.rs index c3c5f528..a267c748 100644 --- a/aquatic_udp/src/config.rs +++ b/aquatic_udp/src/config.rs @@ -1,11 +1,12 @@ use std::{net::SocketAddr, path::PathBuf}; use aquatic_common::{access_list::AccessListConfig, privileges::PrivilegeConfig}; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize}; use aquatic_cli_helpers::LogLevel; +use toml_config::TomlConfig; -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, TomlConfig, Deserialize)] #[serde(default)] pub struct Config { /// Socket workers receive requests from the socket, parse them and send @@ -62,7 +63,7 @@ impl aquatic_cli_helpers::Config for Config { } } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, TomlConfig, Deserialize)] #[serde(default)] pub struct NetworkConfig { /// Bind to this address @@ -108,7 +109,7 @@ impl Default for NetworkConfig { } } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, TomlConfig, Deserialize)] #[serde(default)] pub struct ProtocolConfig { /// Maximum number of torrents to accept in scrape request @@ -129,7 +130,7 @@ impl Default for ProtocolConfig { } } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, TomlConfig, Deserialize)] #[serde(default)] pub struct StatisticsConfig { /// Collect and print/write statistics this often (seconds) @@ -159,7 +160,7 @@ impl Default for StatisticsConfig { } } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, TomlConfig, Deserialize)] #[serde(default)] pub struct CleaningConfig { /// Clean connections this often (seconds) diff --git a/aquatic_udp_bench/Cargo.toml b/aquatic_udp_bench/Cargo.toml index f46b5c3c..8b6d8312 100644 --- a/aquatic_udp_bench/Cargo.toml +++ b/aquatic_udp_bench/Cargo.toml @@ -21,3 +21,4 @@ num-format = "0.4" rand = { version = "0.8", features = ["small_rng"] } rand_distr = "0.4" serde = { version = "1", features = ["derive"] } +toml_config = "0.1.0" diff --git a/aquatic_udp_bench/src/config.rs b/aquatic_udp_bench/src/config.rs index 242b1ea2..00ad62b2 100644 --- a/aquatic_udp_bench/src/config.rs +++ b/aquatic_udp_bench/src/config.rs @@ -1,6 +1,7 @@ -use serde::{Deserialize, Serialize}; +use serde::{Deserialize}; +use toml_config::TomlConfig; -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, TomlConfig, Deserialize)] pub struct BenchConfig { pub num_rounds: usize, pub num_threads: usize, diff --git a/aquatic_udp_load_test/Cargo.toml b/aquatic_udp_load_test/Cargo.toml index 2764137e..17eb8884 100644 --- a/aquatic_udp_load_test/Cargo.toml +++ b/aquatic_udp_load_test/Cargo.toml @@ -24,6 +24,7 @@ rand = { version = "0.8", features = ["small_rng"] } rand_distr = "0.4" serde = { version = "1", features = ["derive"] } socket2 = { version = "0.4", features = ["all"] } +toml_config = "0.1.0" [dev-dependencies] quickcheck = "1" diff --git a/aquatic_udp_load_test/src/config.rs b/aquatic_udp_load_test/src/config.rs index 55ee0e22..3c43da97 100644 --- a/aquatic_udp_load_test/src/config.rs +++ b/aquatic_udp_load_test/src/config.rs @@ -1,12 +1,13 @@ use std::net::SocketAddr; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize}; use aquatic_cli_helpers::LogLevel; #[cfg(feature = "cpu-pinning")] use aquatic_common::cpu_pinning::CpuPinningConfig; +use toml_config::TomlConfig; -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, TomlConfig, Deserialize)] #[serde(default)] pub struct Config { /// Server address @@ -39,7 +40,7 @@ impl Default for Config { } } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, TomlConfig, Deserialize)] #[serde(default)] pub struct NetworkConfig { /// True means bind to one localhost IP per socket. @@ -84,7 +85,7 @@ impl Default for NetworkConfig { } } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, TomlConfig, Deserialize)] #[serde(default)] pub struct RequestConfig { /// Number of torrents to simulate diff --git a/aquatic_ws/Cargo.toml b/aquatic_ws/Cargo.toml index d159bb6c..885e8416 100644 --- a/aquatic_ws/Cargo.toml +++ b/aquatic_ws/Cargo.toml @@ -36,6 +36,7 @@ rustls-pemfile = "0.2" serde = { version = "1", features = ["derive"] } signal-hook = { version = "0.3" } slab = "0.4" +toml_config = "0.1.0" tungstenite = "0.16" # mio diff --git a/aquatic_ws/src/config.rs b/aquatic_ws/src/config.rs index 8b4839e0..b5efc743 100644 --- a/aquatic_ws/src/config.rs +++ b/aquatic_ws/src/config.rs @@ -4,11 +4,12 @@ use std::path::PathBuf; #[cfg(feature = "cpu-pinning")] use aquatic_common::cpu_pinning::CpuPinningConfig; use aquatic_common::{access_list::AccessListConfig, privileges::PrivilegeConfig}; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize}; use aquatic_cli_helpers::LogLevel; +use toml_config::TomlConfig; -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, TomlConfig, Deserialize)] #[serde(default)] pub struct Config { /// Socket workers receive requests from the socket, parse them and send @@ -38,7 +39,7 @@ impl aquatic_cli_helpers::Config for Config { } } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, TomlConfig, Deserialize)] #[serde(default)] pub struct NetworkConfig { /// Bind to this address @@ -56,7 +57,7 @@ pub struct NetworkConfig { pub poll_timeout_microseconds: u64, } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, TomlConfig, Deserialize)] #[serde(default)] pub struct ProtocolConfig { /// Maximum number of torrents to accept in scrape request @@ -68,7 +69,7 @@ pub struct ProtocolConfig { } #[cfg(feature = "with-mio")] -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, TomlConfig, Deserialize)] #[serde(default)] pub struct HandlerConfig { /// Maximum number of requests to receive from channel before locking @@ -77,7 +78,7 @@ pub struct HandlerConfig { pub channel_recv_timeout_microseconds: u64, } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, TomlConfig, Deserialize)] #[serde(default)] pub struct CleaningConfig { /// Clean peers this often (seconds) @@ -98,7 +99,7 @@ pub struct CleaningConfig { } #[cfg(feature = "with-mio")] -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, TomlConfig, Deserialize)] #[serde(default)] pub struct StatisticsConfig { /// Print statistics this often (seconds). Don't print when set to zero. diff --git a/aquatic_ws_load_test/Cargo.toml b/aquatic_ws_load_test/Cargo.toml index e8ba249b..4d7e0598 100644 --- a/aquatic_ws_load_test/Cargo.toml +++ b/aquatic_ws_load_test/Cargo.toml @@ -29,6 +29,7 @@ rand_distr = "0.4" rustls = { version = "0.20", features = ["dangerous_configuration"] } serde = { version = "1", features = ["derive"] } serde_json = "1" +toml_config = "0.1.0" tungstenite = "0.16" [dev-dependencies] diff --git a/aquatic_ws_load_test/src/config.rs b/aquatic_ws_load_test/src/config.rs index 8812562d..3d587cc6 100644 --- a/aquatic_ws_load_test/src/config.rs +++ b/aquatic_ws_load_test/src/config.rs @@ -3,9 +3,10 @@ use std::net::SocketAddr; use aquatic_cli_helpers::LogLevel; #[cfg(feature = "cpu-pinning")] use aquatic_common::cpu_pinning::CpuPinningConfig; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize}; +use toml_config::TomlConfig; -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, TomlConfig, Deserialize)] #[serde(default)] pub struct Config { pub server_address: SocketAddr, @@ -41,7 +42,7 @@ impl Default for Config { } } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, TomlConfig, Deserialize)] #[serde(default)] pub struct TorrentConfig { pub offers_per_request: usize, diff --git a/toml_config/src/lib.rs b/toml_config/src/lib.rs index d3873ad0..161e3ae4 100644 --- a/toml_config/src/lib.rs +++ b/toml_config/src/lib.rs @@ -1,3 +1,4 @@ +pub use toml; pub use toml_config_derive::TomlConfig; /// Run this on your struct implementing TomlConfig to generate a test for it @@ -69,8 +70,9 @@ pub trait TomlConfig: Default { pub mod __private { use std::path::PathBuf; + use std::net::SocketAddr; - pub trait Private: Default { + pub trait Private { fn __to_string(&self, comment: Option, field_name: String) -> String; } @@ -94,8 +96,25 @@ pub mod __private { }; } + impl_trait!(isize); + impl_trait!(i8); + impl_trait!(i16); + impl_trait!(i32); + impl_trait!(i64); + impl_trait!(usize); + impl_trait!(u8); + impl_trait!(u16); + impl_trait!(u32); + impl_trait!(u64); + + impl_trait!(f32); + impl_trait!(f64); + impl_trait!(bool); + impl_trait!(String); + impl_trait!(PathBuf); + impl_trait!(SocketAddr); } diff --git a/toml_config_derive/src/lib.rs b/toml_config_derive/src/lib.rs index 09da5b63..03f09ad1 100644 --- a/toml_config_derive/src/lib.rs +++ b/toml_config_derive/src/lib.rs @@ -1,6 +1,6 @@ use proc_macro2::{TokenStream, TokenTree}; use quote::quote; -use syn::{parse_macro_input, DeriveInput, Type, Attribute, Ident, Data, Fields}; +use syn::{parse_macro_input, DeriveInput, Type, Attribute, Ident, Data, Fields, DataStruct}; #[proc_macro_derive(TomlConfig)] pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { @@ -9,72 +9,96 @@ pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let comment = extract_comment_string(input.attrs); let ident = input.ident; - let mut output_stream = quote! { - let mut output = String::new(); - }; - - extract_from_struct(ident.clone(), input.data, &mut output_stream); - - let expanded = quote! { - impl ::toml_config::TomlConfig for #ident { - fn default_to_string() -> String { + match input.data { + Data::Struct(struct_data) => { + let mut output_stream = quote! { let mut output = String::new(); + }; - let comment: Option = #comment; + extract_from_struct(ident.clone(), struct_data, &mut output_stream); - if let Some(comment) = comment { - output.push_str(&comment); - output.push('\n'); + let expanded = quote! { + impl ::toml_config::TomlConfig for #ident { + fn default_to_string() -> String { + let mut output = String::new(); + + let comment: Option = #comment; + + if let Some(comment) = comment { + output.push_str(&comment); + output.push('\n'); + } + + let body = { + #output_stream + + output + }; + + output.push_str(&body); + + output + } } + impl ::toml_config::__private::Private for #ident { + fn __to_string(&self, comment: Option, field_name: String) -> String { + let mut output = String::new(); - let body = { - #output_stream + output.push('\n'); - output - }; + if let Some(comment) = comment { + output.push_str(&comment); + } + output.push_str(&format!("[{}]\n", field_name)); - output.push_str(&body); + let body = { + #output_stream - output - } - } - impl ::toml_config::__private::Private for #ident { - fn __to_string(&self, comment: Option, field_name: String) -> String { - let mut output = String::new(); + output + }; - output.push('\n'); + output.push_str(&body); - if let Some(comment) = comment { - output.push_str(&comment); + output + } } - output.push_str(&format!("[{}]\n", field_name)); + }; - let body = { - #output_stream - - output - }; - - output.push_str(&body); - - output - } + proc_macro::TokenStream::from(expanded) } - }; + Data::Enum(_) => { + let expanded = quote! { + impl ::toml_config::__private::Private for #ident { + fn __to_string(&self, comment: Option, field_name: String) -> String { + let mut output = String::new(); - proc_macro::TokenStream::from(expanded) + if let Some(comment) = comment { + output.push_str(&comment); + } + + let value = match ::toml_config::toml::ser::to_string(self) { + Ok(value) => value, + Err(err) => panic!("Couldn't serialize enum to toml: {:#}", err), + }; + + output.push_str(&format!("{} = {}\n", field_name, value)); + + output + } + } + }; + + proc_macro::TokenStream::from(expanded) + } + Data::Union(_) => panic!("Unions are not supported"), + } } fn extract_from_struct( struct_ty_ident: Ident, - struct_data: Data, + struct_data: DataStruct, output_stream: &mut TokenStream ) { - let struct_data = if let Data::Struct(data) = struct_data { - data - } else { - panic!("Not a struct"); - }; let fields = if let Fields::Named(fields) = struct_data.fields { fields } else {