From d59c5bbf36e7e4d515f6f120a832f4399d9da107 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Thu, 20 Mar 2025 00:20:43 +0000 Subject: [PATCH] config derive: Switch to derive-deftly Replace our bespoke proc_macro with a derive-deftly macro. The macro is based on one written for derive-adhoc 0.0.1, so could do with a bit of updating. I've inspeccted the changes to the generated output with a semi-manual diff. The only significant changes were changes to `impl InspectableConfigAuto for InstanceConfig`, as follows: > + "link" => &self.link, This is case is handled by the manually-written `impl InspectableConfig for InstanceConfig` so never reaches the derived impl. But it is identical. > + "max_batch_up" => &self.max_batch_up, This seems to have been an omission in the previous code. So the addition seems good. > "max_clock_skew" => &self.max_clock_skew, > - "max_clock_skew" => &self.max_clock_skew, > - "ifname_server" => &self.ifname_server, > "ifname_server" => &self.ifname_server, This is the removal of two duplicates, which arose from the odd code structure in the previous code, which does code gen for inspect_key once per attribute, rather than once per field. Signed-off-by: Ian Jackson --- Cargo.lock | 105 ++++++++++++++++++++++ Cargo.lock.minimal | 141 ++++++++++++++++++++++++++--- Cargo.toml | 1 + debian/control | 1 + macros/macros.rs | 207 +------------------------------------------ src/config.rs | 51 +++++------ src/config_derive.rs | 142 +++++++++++++++++++++++++++++ src/lib.rs | 3 + src/prelude.rs | 1 + 9 files changed, 408 insertions(+), 244 deletions(-) create mode 100644 src/config_derive.rs diff --git a/Cargo.lock b/Cargo.lock index 6cca8af..975b9ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -248,6 +248,34 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "derive-deftly" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0015cb20a284ec944852820598af3aef6309ea8dc317a0304441272ed620f196" +dependencies = [ + "derive-deftly-macros", + "heck", +] + +[[package]] +name = "derive-deftly-macros" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b48e8e38a4aa565da767322b5ca55fb0f8347983c5bc7f7647db069405420479" +dependencies = [ + "heck", + "indexmap", + "itertools", + "proc-macro-crate", + "proc-macro2", + "quote", + "sha3", + "strum", + "syn 2.0.100", + "void", +] + [[package]] name = "digest" version = "0.10.7" @@ -594,6 +622,7 @@ dependencies = [ "base64", "cfg-if", "clap", + "derive-deftly", "easy-ext", "educe", "either", @@ -988,6 +1017,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + [[package]] name = "lazy-regex" version = "3.4.1" @@ -1254,6 +1292,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "proc-macro-crate" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +dependencies = [ + "toml_edit", +] + [[package]] name = "proc-macro2" version = "1.0.94" @@ -1543,6 +1590,16 @@ dependencies = [ "digest", ] +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + [[package]] name = "shlex" version = "1.3.0" @@ -1595,6 +1652,28 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "strum" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.100", +] + [[package]] name = "subtle" version = "2.6.1" @@ -1824,6 +1903,23 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" + +[[package]] +name = "toml_edit" +version = "0.22.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + [[package]] name = "tower" version = "0.5.2" @@ -2246,6 +2342,15 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" +[[package]] +name = "winnow" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" +dependencies = [ + "memchr", +] + [[package]] name = "wit-bindgen-rt" version = "0.39.0" diff --git a/Cargo.lock.minimal b/Cargo.lock.minimal index 5d089fb..0c4cf22 100644 --- a/Cargo.lock.minimal +++ b/Cargo.lock.minimal @@ -217,7 +217,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -293,6 +293,34 @@ dependencies = [ "generic-array", ] +[[package]] +name = "derive-deftly" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39502f680ea10252d02b2809e56239acb3aa68d3659c0e0171ade79c04c48086" +dependencies = [ + "derive-deftly-macros", + "heck", +] + +[[package]] +name = "derive-deftly-macros" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c964673dd1023a358bbc0090f6761aee64dddbd21bc7fa91853e92201110e8f" +dependencies = [ + "heck", + "indexmap 1.8.0", + "itertools", + "proc-macro-crate", + "proc-macro2", + "quote", + "sha3", + "strum", + "syn 2.0.53", + "void", +] + [[package]] name = "digest" version = "0.10.2" @@ -506,7 +534,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -596,13 +624,19 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 2.0.0", "slab", "tokio", "tokio-util", "tracing", ] +[[package]] +name = "hashbrown" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "362385356d610bd1e5a408ddf8d022041774b683f345a1d2cfcb4f60f8ae2db5" + [[package]] name = "hashbrown" version = "0.14.0" @@ -629,6 +663,7 @@ dependencies = [ "base64 0.21.0", "cfg-if 1.0.0", "clap", + "derive-deftly", "easy-ext", "educe", "either", @@ -829,6 +864,16 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0bd112d44d9d870a6819eb505d04dd92b5e4d94bb8c304924a0872ae7016fb5" +[[package]] +name = "indexmap" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" +dependencies = [ + "autocfg 1.1.0", + "hashbrown 0.11.0", +] + [[package]] name = "indexmap" version = "2.0.0" @@ -836,7 +881,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.14.0", ] [[package]] @@ -909,6 +954,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "keccak" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" + [[package]] name = "lazy-regex" version = "2.4.0" @@ -1092,7 +1143,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -1192,6 +1243,16 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe1c37e6347ad1a8351171bee25a92342401f8cd550f76e153724e765ac76bca" +[[package]] +name = "proc-macro-crate" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" +dependencies = [ + "thiserror", + "toml", +] + [[package]] name = "proc-macro2" version = "1.0.75" @@ -1433,6 +1494,17 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustversion" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c48f91977f4ef3be5358c15d131d3f663f6b4d7a112555bf3bf52ad23b6659e5" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.103", +] + [[package]] name = "ryu" version = "1.0.0" @@ -1538,6 +1610,16 @@ dependencies = [ "digest", ] +[[package]] +name = "sha3" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f935e31cf406e8c0e96c2815a5516181b7004ae8c5f296293221e9b1e356bd" +dependencies = [ + "digest", + "keccak", +] + [[package]] name = "signal-hook-registry" version = "1.1.1" @@ -1582,6 +1664,28 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strum" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e96acfc1b70604b8b2f1ffa4c57e59176c7dbb05d556c71ecd2f5498a1dee7f8" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6878079b17446e4d3eba6192bb0a2950d5b14f0ed8424b852310e5a94345d0ef" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 1.0.103", +] + [[package]] name = "subtle" version = "2.5.0" @@ -1601,9 +1705,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.52" +version = "2.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032" dependencies = [ "proc-macro2", "quote", @@ -1698,18 +1802,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.2" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79067843a369b7df0391d33d48636936ee91aa6d6370931eebe1278e6f88a723" +checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.2" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dc6215837a07b345fd86880601a9c55bb5974e0cec507c48bbd3aaa006559a9" +checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0" dependencies = [ "proc-macro2", "quote", @@ -1767,7 +1871,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -1805,6 +1909,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "toml" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a54ae44b0b2c443e7ef6dd3be16a776bae4daa40684f81e15126bc04e7747308" +dependencies = [ + "serde", +] + [[package]] name = "tower-service" version = "0.3.0" @@ -1954,7 +2067,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", "wasm-bindgen-shared", ] @@ -1988,7 +2101,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", "wasm-bindgen-backend", "wasm-bindgen-shared", ] diff --git a/Cargo.toml b/Cargo.toml index 5168959..03de1fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ path="server/server.rs" [dependencies] +derive-deftly = "1" hippotat-macros = { version = "=1.2.1", path = "macros" } backtrace = "0.3.74" diff --git a/debian/control b/debian/control index 2b9d03a..f9aaafd 100644 --- a/debian/control +++ b/debian/control @@ -15,6 +15,7 @@ Build-Depends: debhelper (>= 12), librust-base64-dev (>= 0.21~) , librust-cfg-if-dev (>= 1~) , librust-clap+derive-dev (>= 4~) , + librust-derive-deftly-dev (>= 1~) , librust-easy-ext-dev (>= 1~) , librust-educe-dev (>= 0.4.1~) , librust-either-dev (>= 1.5.1~) , diff --git a/macros/macros.rs b/macros/macros.rs index d010459..4cc50f4 100644 --- a/macros/macros.rs +++ b/macros/macros.rs @@ -13,211 +13,8 @@ #![allow(clippy::map_flatten)] #![allow(clippy::single_char_pattern)] -use syn::{parse_macro_input, parse_quote}; -use syn::{Data, DataStruct, DeriveInput, LitStr, Meta, NestedMeta}; -use quote::{quote, quote_spanned, ToTokens}; -use proc_macro2::{Literal, TokenStream}; - -use std::cell::RefCell; - -use itertools::Itertools; - -/// Generates config resolver method -/// -/// Each field ends up having an SKL and a method. -/// The method actually looks up the value in a particular link context. -/// SKL is passed to the method, which usually uses it to decide which -/// sections to look in. But it is also used by general validation, -/// unconditionally, to reject settings in the wrong section. -/// -/// Atrributes: -/// -/// * `limited`, `server`, `client`: cooked sets of settings; -/// default `SKL` is `PerClient` except for `limited` -/// * `global` and `per_client`: set the SKL. -/// * `special(method, SKL)` -/// -/// Generated code -/// -/// ```rust,ignore -/// impl<'c> ResolveContext<'c> { -/// -/// // SKL here is used by SectionKindList::contains() -/// const FIELDS: &'static [(&'static str, SectionKindList)] = &[ ... ]; -/// -/// #[throws(AE)] -/// fn resolve_instance(&self) -> InstanceConfig { -/// InstanceConfig { -/// ... -/// // SKL here is usually passed to first_of, but the method -/// // can do something more special. -/// max_batch_down: self.limited("max_batch_down", SKL::PerClient)?, -/// ... -/// } -/// } -/// } -/// -/// pub struct InstanceConfigCommon { ... } -/// impl InstanceConfigCommon { -/// pub fn from(l: &[InstanceConfig]) { InstanceConfigCommon { -/// field: ::resolve(l.iter().map(|e| &e.field)), -/// ... -/// } } -/// } -/// ``` -#[proc_macro_derive(ResolveConfig, attributes( - limited, server, client, computed, special, - per_client, global, -))] -pub fn resolve(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let input = parse_macro_input!(input as DeriveInput); - - let (fields, top_ident) = match input { - DeriveInput { - ref ident, - data: Data::Struct(DataStruct { - fields: syn::Fields::Named(ref f), - .. - }), - .. - } => (f, ident), - _ => panic!(), - }; - - let target = &input.ident; - - let mut names = vec![]; - let mut output = vec![]; - let mut global_fields = vec![]; - let mut global_assignments = vec![]; - let mut g_inspects = vec![]; - let mut t_inspects = vec![]; - for field in &fields.named { - //dbg!(field); - let fname = &field.ident.as_ref().unwrap(); - let fname_string = fname.to_string(); - let fname_lit = Literal::string( &fname_string ); - let ty = &field.ty; - let fname_span = fname.span(); - let skl = RefCell::new(None); - let set_skl = |new| { - let mut skl = skl.borrow_mut(); - if let Some(old) = &*skl { panic!("dup SKL {} and {} for field {}", - old, new, &fname); } - *skl = Some(new); - }; - let mut method = quote_spanned!{fname_span=> ordinary }; - for attr in &field.attrs { - let atspan = attr.path.segments.last().unwrap().ident.span(); - if attr.tokens.is_empty() { - let inspect = quote!{ - #fname_lit => &self.#fname, - }; - t_inspects.push(inspect.clone()); - if attr.path == parse_quote!{ per_client } { - set_skl(quote_spanned!{fname_span=> SectionKindList::PerClient }); - continue; - } else if attr.path == parse_quote!{ global } { - set_skl(quote_spanned!{fname_span=> SectionKindList::Global }); - global_fields.push(syn::Field { - attrs: vec![], - ..field.clone() - }); - global_assignments.push(quote_spanned!(fname_span=> - #fname: <#ty as ResolveGlobal>::resolve - (l.iter().map(|e| &e.#fname)), - )); - g_inspects.push(inspect); - continue; - } - method = attr.path.to_token_stream(); - if attr.path == parse_quote!{ limited } { - set_skl(quote_spanned!{atspan=> SectionKindList::Limited }); - } else if attr.path == parse_quote!{ client } { - set_skl(quote_spanned!{atspan=> SectionKindList::PerClient }); - } else if attr.path == parse_quote!{ computed } { - set_skl(quote_spanned!{atspan=> SectionKindList::None }); - } - } else if attr.path == parse_quote!{ special } { - let meta = match attr.parse_meta().unwrap() { - Meta::List(list) => list, - _ => panic!(), - }; - let (tmethod, tskl) = meta.nested.iter().collect_tuple().unwrap(); - fn get_path(meta: &NestedMeta) -> TokenStream { - match meta { - NestedMeta::Meta(Meta::Path(ref path)) => path.to_token_stream(), - _ => panic!(), - } - } - method = get_path(tmethod); - *skl.borrow_mut() = Some(get_path(tskl)); - } - } - let skl = skl.into_inner() - .expect(&format!("SKL not specified! (field {})!", fname)); - - names.push(quote!{ - (#fname_lit, #skl), - }); - //dbg!(&method); - output.push(quote!{ - #fname: rctx. #method ( #fname_lit, #skl )?, - }); - //eprintln!("{:?} method={:?} skl={:?}", field.ident, method, skl); - } - //dbg!(&output); - - let global = syn::Ident::new(&format!("{}Global", top_ident), - top_ident.span()); - - let mk_inspects = |self_, inspects: Vec<_>| quote! { - impl InspectableConfigAuto for #self_ { - fn inspect_key_auto(&self, field: &'_ str) - -> Option<&dyn InspectableConfigValue> { - Some(match field { - #( #inspects )* - _ => return None, - }) - } - } - }; - let g_inspects = mk_inspects(&global, g_inspects); - let t_inspects = mk_inspects(&target, t_inspects); - - let output = quote! { - impl #target { - const FIELDS: &'static [(&'static str, SectionKindList)] - = &[ #( #names )* ]; - - fn resolve_instance(rctx: &ResolveContext) - -> ::std::result::Result<#target, anyhow::Error> - { - ::std::result::Result::Ok(#target { - #( #output )* - }) - } - } - - #t_inspects - - #[derive(Debug)] - pub struct #global { - #( #global_fields ),* - } - - impl #global { - pub fn from(l: &[#top_ident]) -> #global { #global { - #( #global_assignments )* - } } - } - - #g_inspects - }; - - //eprintln!("{}", &output); - output.into() -} +use syn::LitStr; +use quote::quote; #[proc_macro] pub fn into_crlfs(input: proc_macro::TokenStream) -> proc_macro::TokenStream { diff --git a/src/config.rs b/src/config.rs index df80d1e..10771b2 100644 --- a/src/config.rs +++ b/src/config.rs @@ -4,44 +4,45 @@ use crate::prelude::*; -#[derive(hippotat_macros::ResolveConfig)] #[derive(Debug,Clone)] +#[derive(Deftly)] +#[derive_deftly(InspectableConfigAuto, InstanceConfig)] pub struct InstanceConfig { // Exceptional settings - #[special(special_link, SKL::None)] pub link: LinkName, - #[per_client] pub secret: Secret, - #[global] #[special(special_ipif, SKL::PerClient)] pub ipif: String, + #[deftly(special="link", skl="SKL::None")] pub link: LinkName, + #[deftly(per_client)] pub secret: Secret, + #[deftly(global, special="ipif", skl="SKL::PerClient")] pub ipif: String, // Capped settings: - #[limited] pub max_batch_down: u32, - #[limited] pub max_queue_time: Duration, - #[limited] pub http_timeout: Duration, - #[limited] pub target_requests_outstanding: u32, - #[special(special_max_up, SKL::Limited)] pub max_batch_up: u32, + #[deftly(limited)] pub max_batch_down: u32, + #[deftly(limited)] pub max_queue_time: Duration, + #[deftly(limited)] pub http_timeout: Duration, + #[deftly(limited)] pub target_requests_outstanding: u32, + #[deftly(special="max_up", skl="SKL::Limited")] pub max_batch_up: u32, // Ordinary settings, used by both, not client-specifi: - #[global] pub addrs: Vec, - #[global] pub vnetwork: Vec, - #[global] pub vaddr: IpAddr, - #[global] pub vrelay: IpAddr, - #[global] pub port: u16, - #[global] pub mtu: u32, + #[deftly(global)] pub addrs: Vec, + #[deftly(global)] pub vnetwork: Vec, + #[deftly(global)] pub vaddr: IpAddr, + #[deftly(global)] pub vrelay: IpAddr, + #[deftly(global)] pub port: u16, + #[deftly(global)] pub mtu: u32, // Ordinary settings, used by server only: - #[server] #[per_client] pub max_clock_skew: Duration, - #[server] #[global] pub ifname_server: String, + #[deftly(server, per_client)] pub max_clock_skew: Duration, + #[deftly(server, global)] pub ifname_server: String, // Ordinary settings, used by client only: - #[client] pub http_timeout_grace: Duration, - #[client] pub max_requests_outstanding: u32, - #[client] pub http_retry: Duration, - #[client] pub success_report_interval: Duration, - #[client] pub url: Url, - #[client] pub vroutes: Vec, - #[client] pub ifname_client: String, + #[deftly(client)] pub http_timeout_grace: Duration, + #[deftly(client)] pub max_requests_outstanding: u32, + #[deftly(client)] pub http_retry: Duration, + #[deftly(client)] pub success_report_interval: Duration, + #[deftly(client)] pub url: Url, + #[deftly(client)] pub vroutes: Vec, + #[deftly(client)] pub ifname_client: String, // Computed, rather than looked up. Client only: - #[computed] pub effective_http_timeout: Duration, + #[deftly(computed)] pub effective_http_timeout: Duration, } static DEFAULT_CONFIG: &str = r#" diff --git a/src/config_derive.rs b/src/config_derive.rs new file mode 100644 index 0000000..aa42c95 --- /dev/null +++ b/src/config_derive.rs @@ -0,0 +1,142 @@ + +use crate::prelude::*; + +define_derive_deftly! { + /// Implements `InspectableConfigAuto` + InspectableConfigAuto for struct, expect items: + + impl InspectableConfigAuto for $ttype { + fn inspect_key_auto(&self, field: &'_ str) + -> Option<&dyn InspectableConfigValue> { + Some(match field { + $( + stringify!($fname) => &self.$fname, + ) + _ => return None, + }) + } + } +} + +define_derive_deftly! { + /// Generates config resolver method, only for `InstanceConfig` + /// + /// Each field ends up having an SKL and a method. + /// The method actually looks up the value in a particular link context. + /// SKL is passed to the method, which usually uses it to decide which + /// sections to look in. But it is also used by general validation, + /// unconditionally, to reject settings in the wrong section. + /// + /// Atrributes: + /// + /// * `limited`, `server`, `client`: cooked sets of settings; + /// default `SKL` is `PerClient` except for `limited` + /// * `global` and `per_client`: set the SKL. + /// * `special(method, SKL)` + /// + /// Generated code + /// + /// ```rust,ignore + /// impl<'c> ResolveContext<'c> { + /// + /// // SKL here is used by SectionKindList::contains() + /// const FIELDS: &'static [(&'static str, SectionKindList)] = &[ ... ]; + /// + /// #[throws(AE)] + /// fn resolve_instance(&self) -> InstanceConfig { + /// InstanceConfig { + /// ... + /// // SKL here is usually passed to first_of, but the method + /// // can do something more special. + /// max_batch_down: self.limited("max_batch_down", SKL::PerClient)?, + /// ... + /// } + /// } + /// } + /// + /// pub struct InstanceConfigCommon { ... } + /// impl InstanceConfigCommon { + /// pub fn from(l: &[InstanceConfig]) { InstanceConfigCommon { + /// field: ::resolve(l.iter().map(|e| &e.field)), + /// ... + /// } } + /// } + /// ``` + + InstanceConfig expect items: + + struct InstanceConfigSKLs { $( + $fname: SectionKindList, + ) } + const FIELD_SKLS: InstanceConfigSKLs = InstanceConfigSKLs { + $( + $fname: ${if fmeta(skl) { + ${fmeta(skl) as expr} + } else { + ${select1 + fmeta( per_client ) { SKL::PerClient } + fmeta( client ) { SKL::PerClient } + fmeta( global ) { SKL::Global } + fmeta( limited ) { SKL::Limited } + fmeta( computed ) { SKL::None } + } + }} + , + ) + }; + + impl InstanceConfig { + const FIELDS : & 'static [(& 'static str, SectionKindList)] = &[ $( + ( + stringify!($fname), + FIELD_SKLS.$fname, + ), + ) ]; + + #[throws(AE)] + fn resolve_instance(rctx: &ResolveContext) -> InstanceConfig { + InstanceConfig { + $( + $fname: rctx. + ${if fmeta(special) { + ${paste special_ ${fmeta(special)}} + } else { + ${select1 + fmeta( server ) { server } + fmeta( client ) { client } + fmeta( limited ) { limited } + fmeta( computed ) { computed } + else { ordinary } + } + }} + ( + stringify!($fname), + FIELD_SKLS.$fname, + )?, + ) + } + } + } + + #[derive(Debug)] + #[derive(Deftly)] + #[derive_deftly(InspectableConfigAuto)] + pub struct InstanceConfigGlobal { + $( + ${when fmeta(global)} + pub $fname: $ftype, + ) + } + + impl InstanceConfigGlobal { + pub fn from(l: &[InstanceConfig]) -> InstanceConfigGlobal { + InstanceConfigGlobal { + $( + ${when fmeta(global)} + $fname: <$ftype as ResolveGlobal> + ::resolve(l.iter().map(|e| &e.$fname)), + ) + } + } + } +} diff --git a/src/lib.rs b/src/lib.rs index e7f2ca5..946b757 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,6 +21,9 @@ pub mod prelude; +#[macro_use] +pub mod config_derive; + pub mod compat; pub mod config; pub mod ipif; diff --git a/src/prelude.rs b/src/prelude.rs index c2adfbb..66d3733 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -28,6 +28,7 @@ pub use std::sync::Arc; pub use std::task::Poll; pub use std::time::{SystemTime, UNIX_EPOCH}; +pub use derive_deftly::{define_derive_deftly, Deftly}; pub use educe::Educe; pub use either::Either; pub use easy_ext::ext; -- 2.30.2