diff --git a/foundations/examples/Cargo.toml b/foundations/examples/Cargo.toml index 995fb6a2..1a3849db 100644 --- a/foundations/examples/Cargo.toml +++ b/foundations/examples/Cargo.toml @@ -12,6 +12,10 @@ path = "src/simple.rs" name = "http" path = "src/http.rs" +[[example]] +name = "generics" +path = "src/generics.rs" + [dependencies] scuffle-foundations = { path = "../" } tracing = "0.1" diff --git a/foundations/examples/src/generics.rs b/foundations/examples/src/generics.rs new file mode 100644 index 00000000..f69f373c --- /dev/null +++ b/foundations/examples/src/generics.rs @@ -0,0 +1,20 @@ +use scuffle_foundations::settings::{auto_settings, Settings}; + +#[auto_settings] +pub struct BaseSettings { + #[serde(flatten)] + /// The internal settings. + external: S, +} + +#[auto_settings] +pub struct ExtraSettings { + /// An extra setting. + pub extra: bool, + /// Another extra setting. + pub another: bool, +} + +fn main() { + println!("{}", BaseSettings::::default().to_yaml_string().unwrap()); +} diff --git a/foundations/examples/src/http.rs b/foundations/examples/src/http.rs index ec7701dc..7746791f 100644 --- a/foundations/examples/src/http.rs +++ b/foundations/examples/src/http.rs @@ -11,7 +11,7 @@ use scuffle_foundations::runtime::spawn; use scuffle_foundations::settings::cli::Matches; use scuffle_foundations::telementry::opentelemetry::OpenTelemetrySpanExt; use scuffle_foundations::telementry::settings::TelementrySettings; -use scuffle_foundations::{settings::settings, wrapped, BootstrapResult}; +use scuffle_foundations::{settings::auto_settings, wrapped, BootstrapResult}; use std::convert::Infallible; use std::net::SocketAddr; use std::net::TcpListener as StdTcpListener; @@ -19,7 +19,7 @@ use tokio::net::{TcpListener, TcpStream}; type Body = http_body_util::Full; -#[settings] +#[auto_settings] #[serde(default)] struct Config { telemetry: TelementrySettings, diff --git a/foundations/examples/src/simple.rs b/foundations/examples/src/simple.rs index 546e5f66..3d7a1082 100644 --- a/foundations/examples/src/simple.rs +++ b/foundations/examples/src/simple.rs @@ -1,6 +1,6 @@ use scuffle_foundations::{ bootstrap::{bootstrap, Bootstrap, RuntimeSettings}, - settings::{cli::Matches, settings}, + settings::{cli::Matches, auto_settings}, telementry::{metrics::metrics, settings::TelementrySettings}, }; @@ -24,7 +24,7 @@ mod http_server { pub fn requests_failed_total(endpoint_name: &Arc, status_code: u16) -> Counter; } -#[settings] +#[auto_settings] pub struct HttpServerSettings { /// Telementry Settings telementry: TelementrySettings, diff --git a/foundations/macros/src/settings/types/field_ty.rs b/foundations/macros/src/settings/types/field_ty.rs index b60514f1..1b8e0498 100644 --- a/foundations/macros/src/settings/types/field_ty.rs +++ b/foundations/macros/src/settings/types/field_ty.rs @@ -3,7 +3,7 @@ use syn::{LitStr, Meta}; use crate::helpers::parse_docs; use super::{ - serde::{parse_default_fn, Name, RenameAll}, + serde::{parse_default_fn, serde_flatten, Name, RenameAll}, Args, }; @@ -14,6 +14,7 @@ pub struct Field { pub default_fn: Option, pub args: FieldArgs, pub item: syn::Field, + pub flatten: bool, } impl Field { @@ -27,6 +28,7 @@ impl Field { docs: parse_docs(&item.attrs), default_fn: parse_default_fn(&item.attrs)?, args: FieldArgs::parse(&item.attrs)?, + flatten: serde_flatten(&item.attrs)?, item, }) } diff --git a/foundations/macros/src/settings/types/serde.rs b/foundations/macros/src/settings/types/serde.rs index 70a6fe3b..207116e8 100644 --- a/foundations/macros/src/settings/types/serde.rs +++ b/foundations/macros/src/settings/types/serde.rs @@ -163,6 +163,13 @@ pub fn parse_default_fn(attrs: &[syn::Attribute]) -> syn::Result syn::Result { + parse_serde_attrs(attrs, false, |state, meta| match meta { + Meta::Path(meta) if meta.is_ident("flatten") => *state = true, + _ => {} + }) +} + fn parse_serde_attrs( attr: &[syn::Attribute], state: T, diff --git a/foundations/macros/src/settings/types/struct_ty.rs b/foundations/macros/src/settings/types/struct_ty.rs index cf532faf..2c283ef0 100644 --- a/foundations/macros/src/settings/types/struct_ty.rs +++ b/foundations/macros/src/settings/types/struct_ty.rs @@ -100,7 +100,7 @@ impl Struct { }) .collect::>(); - let insert = if docs.is_empty() { + let insert = if docs.is_empty() || field.flatten { quote::quote! {} } else { quote::quote! { @@ -111,10 +111,18 @@ impl Struct { let name = &name.serialize; let ident = field.item.ident.as_ref().unwrap(); + let name_push = if field.flatten { + quote::quote! {} + } else { + quote::quote! { + parent_key.push(::std::borrow::Cow::Borrowed(#name)); + } + }; + quote::quote! { { let mut parent_key = parent_key.to_vec(); - parent_key.push(::std::borrow::Cow::Borrowed(#name)); + #name_push (&&&&#crate_path::settings::Wrapped(&self.#ident)).add_docs(&parent_key, docs); #insert } diff --git a/foundations/macros/src/settings/types/variant_ty.rs b/foundations/macros/src/settings/types/variant_ty.rs index 35358ee1..472020b9 100644 --- a/foundations/macros/src/settings/types/variant_ty.rs +++ b/foundations/macros/src/settings/types/variant_ty.rs @@ -91,7 +91,7 @@ impl Variant { }) .collect::>(); - let insert = if docs.is_empty() { + let insert = if docs.is_empty() || field.flatten { quote::quote! {} } else { quote::quote! { @@ -99,7 +99,7 @@ impl Variant { } }; - let name_push = if self.fields.len() == 1 && field.name.is_none() { + let name_push = if (self.fields.len() == 1 && field.name.is_none()) || field.flatten { quote::quote! {} } else { let name = field diff --git a/foundations/src/settings/mod.rs b/foundations/src/settings/mod.rs index 1cd19f36..f30f5208 100644 --- a/foundations/src/settings/mod.rs +++ b/foundations/src/settings/mod.rs @@ -7,7 +7,7 @@ use serde_yaml::Value; pub mod cli; #[cfg(feature = "macros")] -pub use scuffle_foundations_macros::{auto_settings as settings, Settings}; +pub use scuffle_foundations_macros::{auto_settings, Settings}; #[derive(Debug, Clone)] pub struct SettingsParser { diff --git a/foundations/src/settings/traits.rs b/foundations/src/settings/traits.rs index 2ef4b346..f69de15f 100644 --- a/foundations/src/settings/traits.rs +++ b/foundations/src/settings/traits.rs @@ -153,3 +153,5 @@ where (**self.0).add_docs(parent_key, docs); } } + +impl Settings for () {} diff --git a/foundations/src/telementry/settings.rs b/foundations/src/telementry/settings.rs index c141e5cf..871942db 100644 --- a/foundations/src/telementry/settings.rs +++ b/foundations/src/telementry/settings.rs @@ -13,7 +13,7 @@ use opentelemetry_sdk::Resource; #[cfg(feature = "logging")] use tracing_subscriber::fmt::time::{ChronoLocal, ChronoUtc}; -#[crate::settings::settings(crate_path = "crate")] +#[crate::settings::auto_settings(crate_path = "crate")] pub struct TelementrySettings { /// Settings for metric exporting. pub metrics: MetricsSettings, @@ -30,7 +30,7 @@ pub struct TelementrySettings { } #[cfg(feature = "metrics")] -#[crate::settings::settings(crate_path = "crate")] +#[crate::settings::auto_settings(crate_path = "crate")] pub struct MetricsSettings { /// Whether to enable metrics. #[settings(default = true)] @@ -40,7 +40,7 @@ pub struct MetricsSettings { } #[cfg(feature = "opentelemetry")] -#[crate::settings::settings(crate_path = "crate")] +#[crate::settings::auto_settings(crate_path = "crate")] #[serde(default)] pub struct OpentelemetrySettings { /// Whether to enable opentelemetry span exporting. @@ -98,7 +98,7 @@ pub struct OpentelemetrySettings { } #[cfg(feature = "opentelemetry")] -#[crate::settings::settings(crate_path = "crate")] +#[crate::settings::auto_settings(crate_path = "crate")] #[serde(default)] pub struct OpentelemetrySettingsLogging { #[settings(default = OpentelemetrySettingsLoggingLevel::Warn)] @@ -110,7 +110,7 @@ pub struct OpentelemetrySettingsLogging { } #[cfg(feature = "opentelemetry")] -#[crate::settings::settings(crate_path = "crate")] +#[crate::settings::auto_settings(crate_path = "crate")] #[serde(rename_all = "lowercase")] pub enum OpentelemetrySettingsLoggingLevel { /// Error level logging. @@ -129,7 +129,7 @@ pub enum OpentelemetrySettingsLoggingLevel { } #[cfg(feature = "opentelemetry")] -#[crate::settings::settings(crate_path = "crate")] +#[crate::settings::auto_settings(crate_path = "crate")] #[serde(rename_all = "lowercase")] pub enum OpentelemetrySettingsExportMethod { #[settings(default)] @@ -140,7 +140,7 @@ pub enum OpentelemetrySettingsExportMethod { } #[cfg(feature = "opentelemetry")] -#[crate::settings::settings(crate_path = "crate")] +#[crate::settings::auto_settings(crate_path = "crate")] #[serde(rename_all = "lowercase")] pub enum OpentelemetrySettingsSampler { /// Always sample all spans. @@ -174,7 +174,7 @@ fn default_true() -> bool { } #[cfg(feature = "logging")] -#[crate::settings::settings(crate_path = "crate")] +#[crate::settings::auto_settings(crate_path = "crate")] #[serde(default)] pub struct LoggingSettings { /// Whether to enable logging. @@ -209,7 +209,7 @@ pub struct LoggingSettings { } #[cfg(feature = "logging")] -#[crate::settings::settings(crate_path = "crate")] +#[crate::settings::auto_settings(crate_path = "crate")] #[serde(rename_all = "lowercase")] pub enum LoggingSettingsTimestamps { /// Show timestamps in logs in the local timezone. @@ -222,7 +222,7 @@ pub enum LoggingSettingsTimestamps { } #[cfg(feature = "logging")] -#[crate::settings::settings(crate_path = "crate")] +#[crate::settings::auto_settings(crate_path = "crate")] #[serde(rename_all = "lowercase")] pub enum LoggingSettingsFormat { #[settings(default)] @@ -240,7 +240,7 @@ pub enum LoggingSettingsFormat { any(feature = "pprof-cpu", feature = "pprof-heap", feature = "metrics",), feature = "telemetry-server" ))] -#[crate::settings::settings(crate_path = "crate")] +#[crate::settings::auto_settings(crate_path = "crate")] #[serde(default)] pub struct ServerSettings { /// Whether to enable the server.