chiark / gitweb /
config derive: Switch to derive-deftly
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Thu, 20 Mar 2025 00:20:43 +0000 (00:20 +0000)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Thu, 20 Mar 2025 00:23:05 +0000 (00:23 +0000)
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 <ijackson@chiark.greenend.org.uk>
Cargo.lock
Cargo.lock.minimal
Cargo.toml
debian/control
macros/macros.rs
src/config.rs
src/config_derive.rs [new file with mode: 0644]
src/lib.rs
src/prelude.rs

index 6cca8af1dc72e86a6c4d397ef713a272f0393066..975b9eda47f28a102061b34f4317bdfb7e19b261 100644 (file)
@@ -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"
index 5d089fb45256ae7db03cc601d3e3438d387ead69..0c4cf22b527827749431fb23b7bd76af78eb3b45 100644 (file)
@@ -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",
 ]
index 51689591e029bde3c4002781d7bb092c6680d943..03de1feab0d0d4560cf05dc2b7ac7c5ab0bd8aa0 100644 (file)
@@ -26,6 +26,7 @@ path="server/server.rs"
 
 [dependencies]
 
+derive-deftly = "1"
 hippotat-macros = { version = "=1.2.1", path = "macros" }
 
 backtrace = "0.3.74"
index 2b9d03a7b5112653b418a14e857317f49368f7ad..f9aaafdad68d72a4688f7655a7223c3d43b9ce6a 100644 (file)
@@ -15,6 +15,7 @@ Build-Depends: debhelper (>= 12),
     librust-base64-dev (>= 0.21~) <!upstream-cargo>,
     librust-cfg-if-dev (>= 1~) <!upstream-cargo>,
     librust-clap+derive-dev (>= 4~) <!upstream-cargo>,
+    librust-derive-deftly-dev (>= 1~) <!upstream-cargo>,
     librust-easy-ext-dev (>= 1~) <!upstream-cargo>,
     librust-educe-dev (>= 0.4.1~) <!upstream-cargo>,
     librust-either-dev (>= 1.5.1~) <!upstream-cargo>,
index d0104595b07249b3b8463c116567643f4db72a9d..4cc50f4505effe03c1adbb0ebebac92cafcb9024 100644 (file)
 #![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: <Type as ResolveGlobal>::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 {
index df80d1ea870f56d55faf57ed85160abe9cffa8d7..10771b2babf9099197b6657242cbd22aa6ce56d1 100644 (file)
@@ -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<IpAddr>,
-  #[global]  pub vnetwork:                     Vec<IpNet>,
-  #[global]  pub vaddr:                        IpAddr,
-  #[global]  pub vrelay:                       IpAddr,
-  #[global]  pub port:                         u16,
-  #[global]  pub mtu:                          u32,
+  #[deftly(global)]  pub addrs:                        Vec<IpAddr>,
+  #[deftly(global)]  pub vnetwork:                     Vec<IpNet>,
+  #[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<IpNet>,
-  #[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<IpNet>,
+  #[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 (file)
index 0000000..aa42c95
--- /dev/null
@@ -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: <Type as ResolveGlobal>::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)),
+        )
+      }
+    }
+  }
+}
index e7f2ca5b8e215a24b81f94f2652719c1904d7fbf..946b757f7688f2ec463c29c17a9c110578ac45f1 100644 (file)
@@ -21,6 +21,9 @@
 
 pub mod prelude;
 
+#[macro_use]
+pub mod config_derive;
+
 pub mod compat;
 pub mod config;
 pub mod ipif;
index c2adfbb481c637b75beb7a17eaefe3668332fd87..66d3733a4ca86bfddc7878ba86b82fa17ac7bc4c 100644 (file)
@@ -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;