From b08f2142477f3546a1d71b4b82e8e1d26e6c09fe Mon Sep 17 00:00:00 2001 From: tenuous-guidance <105654822+tenuous-guidance@users.noreply.github.com> Date: Tue, 8 Apr 2025 17:23:53 +0100 Subject: [PATCH 1/3] refactor(derive)!: replace forward_serde with forward --- confik-macros/src/lib.rs | 34 +++++++++---------- confik/CHANGELOG.md | 9 +++++ confik/src/lib.md | 8 +++-- .../tests/{serde_forward => forward}/mod.rs | 10 +++--- confik/tests/main.rs | 4 +-- 5 files changed, 38 insertions(+), 27 deletions(-) rename confik/tests/{serde_forward => forward}/mod.rs (86%) diff --git a/confik-macros/src/lib.rs b/confik-macros/src/lib.rs index d405fdb..b6f361f 100644 --- a/confik-macros/src/lib.rs +++ b/confik-macros/src/lib.rs @@ -66,13 +66,13 @@ impl FromMeta for FieldTryFrom { } } -/// Handles requesting to forward `serde` attributes. +/// Handles requesting to forward attributes. #[derive(Debug)] -struct ForwardSerde { +struct Forward { items: Vec<NestedMeta>, } -impl ToTokens for ForwardSerde { +impl ToTokens for Forward { fn into_token_stream(self) -> TokenStream { self.to_token_stream() } @@ -83,11 +83,11 @@ impl ToTokens for ForwardSerde { fn to_token_stream(&self) -> TokenStream { let Self { items } = self; - quote!(#[serde(#( #items ),*)]) + quote!(#( #[ #items ] )*) } } -impl FromMeta for ForwardSerde { +impl FromMeta for Forward { fn from_list(items: &[NestedMeta]) -> darling::Result<Self> { let items = items.to_vec(); @@ -129,8 +129,8 @@ struct VariantImplementer { /// Optional explicit override of the variant's discriminant. discriminant: Option<Expr>, - /// Optional attributes to forward to serde. - forward_serde: Option<ForwardSerde>, + /// Optional attributes to forward to the builder's variant. + forward: Option<Forward>, } impl VariantImplementer { @@ -140,7 +140,7 @@ impl VariantImplementer { ident, fields, discriminant, - forward_serde, + forward, } = var_impl.as_ref(); let field_vec = fields @@ -154,7 +154,7 @@ impl VariantImplementer { .map(|disc| quote_spanned!(disc.span() => = discriminant)); Ok(quote_spanned! { var_impl.span() => - #forward_serde + #forward #ident #fields #discriminant }) } @@ -330,8 +330,8 @@ struct FieldImplementer { /// The field type. ty: Type, - /// Optional attributes to forward to serde. - forward_serde: Option<ForwardSerde>, + /// Optional attributes to forward to the builder's field. + forward: Option<Forward>, } impl FieldImplementer { @@ -380,7 +380,7 @@ impl FieldImplementer { ty, ident, secret, - forward_serde, + forward, from, try_from, .. @@ -414,7 +414,7 @@ impl FieldImplementer { Ok(quote_spanned! { ident.span() => #[serde(default)] - #forward_serde + #forward #ident #ty }) } @@ -623,8 +623,8 @@ struct RootImplementer { /// `pub`, `pub(crate)`, etc. vis: Visibility, - /// Optional attributes to forward to serde. - forward_serde: Option<ForwardSerde>, + /// Optional attributes to forward to the builder struct/enum. + forward: Option<Forward>, /// Derives needed by the builder, e.g. `Hash`. derive: Option<Derive>, @@ -665,7 +665,7 @@ impl RootImplementer { data, generics, vis, - forward_serde, + forward, derive: additional_derives, .. } = self; @@ -727,7 +727,7 @@ impl RootImplementer { Ok(quote_spanned! { target_name.span() => #[derive(::std::default::Default, ::confik::__exports::__serde::Deserialize, #additional_derives )] #[serde(crate = "::confik::__exports::__serde")] - #forward_serde + #forward #vis #enum_or_struct_token #builder_name #type_generics #where_clause #bracketed_data #terminator diff --git a/confik/CHANGELOG.md b/confik/CHANGELOG.md index f947c0a..92e551d 100644 --- a/confik/CHANGELOG.md +++ b/confik/CHANGELOG.md @@ -3,6 +3,15 @@ ## Unreleased - Implement `Configuration` for [`js_option::JsOption`](https://docs.rs/js_option/0.1.1/js_option/enum.JsOption.html) +- Add a new `confik(forward(...))` attribute. As well as allowing for forwarding general attributes to the builder, this: + - Replaces `confik(forward_serde(...))`. E.g. + ```rust + #[derive(Configuration)] + struct Config { + #[confik(forward(serde(default)))] + num: usize, + } + ``` ## 0.13.0 diff --git a/confik/src/lib.md b/confik/src/lib.md index ac64049..21806bd 100644 --- a/confik/src/lib.md +++ b/confik/src/lib.md @@ -76,20 +76,22 @@ struct Config { } ``` -### Forwarding Attributes To `Deserialize` +### Forwarding Attributes -The serde attributes used for customizing a `Deserialize` derive typically are achieved by adding `#[confik(forward_serde(...))` attributes. +The serde attributes used for customizing a `Deserialize` derive typically are achieved by adding `#[confik(forward(serde(...)))]` attributes. For example: ``` #[derive(confik::Configuration)] struct Config { - #[confik(forward_serde(rename = "other_data"))] + #[confik(forward(serde(rename = "other_data")))] data: usize, } ``` +This can also be used for non-serde attributes, but this is less commonly needed. + ### Defaults Defaults are specified on a per-field basis. diff --git a/confik/tests/serde_forward/mod.rs b/confik/tests/forward/mod.rs similarity index 86% rename from confik/tests/serde_forward/mod.rs rename to confik/tests/forward/mod.rs index 35e0e9d..1d77ca4 100644 --- a/confik/tests/serde_forward/mod.rs +++ b/confik/tests/forward/mod.rs @@ -1,22 +1,22 @@ use confik::Configuration; #[derive(Configuration, Debug, PartialEq, Eq)] -#[confik(forward_serde(rename_all = "UPPERCASE"))] +#[confik(forward(serde(rename_all = "UPPERCASE")))] struct Container { field: usize, } #[derive(Configuration, Debug, PartialEq, Eq)] struct Inner { - #[confik(forward_serde(rename = "outer"))] + #[confik(forward(serde(rename = "outer")))] inner: usize, } #[derive(Configuration, Debug, PartialEq, Eq)] struct Field { - #[confik(forward_serde(rename = "other_name"))] + #[confik(forward(serde(rename = "other_name")))] field1: usize, - #[confik(forward_serde(flatten))] + #[confik(forward(serde(flatten)))] field2: Inner, } @@ -25,7 +25,7 @@ enum Clothes { Hat, // Put some data in to force use of a custom builder Scarf(usize), - #[confik(forward_serde(alias = "Gloves", alias = "SomethingElse"))] + #[confik(forward(serde(alias = "Gloves", alias = "SomethingElse")))] Other, } diff --git a/confik/tests/main.rs b/confik/tests/main.rs index 35abf88..57b8151 100644 --- a/confik/tests/main.rs +++ b/confik/tests/main.rs @@ -3,11 +3,11 @@ mod array; mod common; mod complex_enums; mod defaulting_containers; +mod forward; mod keyed_containers; mod option_builder; mod secret; mod secret_option; -mod serde_forward; mod singly_nested_tests; mod third_party; mod unkeyed_containers; @@ -84,7 +84,7 @@ mod toml { fn from_humantime() { #[derive(Debug, PartialEq, Eq, Configuration)] struct Config { - #[confik(forward_serde(with = "humantime_serde"))] + #[confik(forward(serde(with = "humantime_serde")))] timeout: Duration, } From 5a7ffe0b91aa02546b6f28ab20d821184a2e0e8d Mon Sep 17 00:00:00 2001 From: tenuous-guidance <105654822+tenuous-guidance@users.noreply.github.com> Date: Tue, 8 Apr 2025 17:35:45 +0100 Subject: [PATCH 2/3] refactor(derive)!: replace derive with forward --- confik-macros/src/lib.rs | 9 ++--- confik-macros/tests/trybuild.rs | 2 +- confik-macros/tests/trybuild/19-derive.rs | 2 +- .../tests/trybuild/23-where-clause.rs | 2 +- .../tests/trybuild/fail-derive-literal.stderr | 5 --- ...ive-literal.rs => fail-forward-literal.rs} | 2 +- .../trybuild/fail-forward-literal.stderr | 17 ++++++++ confik/CHANGELOG.md | 6 +++ confik/examples/derives.rs | 2 +- confik/src/common.rs | 2 +- confik/src/lib.md | 40 ++++++++++++++----- confik/tests/keyed_containers/mod.rs | 2 +- confik/tests/unkeyed_containers/mod.rs | 2 +- 13 files changed, 64 insertions(+), 29 deletions(-) delete mode 100644 confik-macros/tests/trybuild/fail-derive-literal.stderr rename confik-macros/tests/trybuild/{fail-derive-literal.rs => fail-forward-literal.rs} (62%) create mode 100644 confik-macros/tests/trybuild/fail-forward-literal.stderr diff --git a/confik-macros/src/lib.rs b/confik-macros/src/lib.rs index b6f361f..02047a4 100644 --- a/confik-macros/src/lib.rs +++ b/confik-macros/src/lib.rs @@ -624,10 +624,10 @@ struct RootImplementer { vis: Visibility, /// Optional attributes to forward to the builder struct/enum. + /// + /// This can be serde attributes e.g. `#[confik(forward(serde(default)))]` but also others like + /// `#[confik(forward(derive(Hash)))]` forward: Option<Forward>, - - /// Derives needed by the builder, e.g. `Hash`. - derive: Option<Derive>, } impl RootImplementer { @@ -666,7 +666,6 @@ impl RootImplementer { generics, vis, forward, - derive: additional_derives, .. } = self; @@ -725,7 +724,7 @@ impl RootImplementer { let (_impl_generics, type_generics, where_clause) = generics.split_for_impl(); Ok(quote_spanned! { target_name.span() => - #[derive(::std::default::Default, ::confik::__exports::__serde::Deserialize, #additional_derives )] + #[derive(::std::default::Default, ::confik::__exports::__serde::Deserialize)] #[serde(crate = "::confik::__exports::__serde")] #forward #vis #enum_or_struct_token #builder_name #type_generics #where_clause diff --git a/confik-macros/tests/trybuild.rs b/confik-macros/tests/trybuild.rs index 64b8d1a..d89c8cd 100644 --- a/confik-macros/tests/trybuild.rs +++ b/confik-macros/tests/trybuild.rs @@ -34,7 +34,7 @@ fn compile_macros() { t.compile_fail("tests/trybuild/fail-default-invalid-expr.rs"); t.compile_fail("tests/trybuild/fail-config-name-value.rs"); t.compile_fail("tests/trybuild/fail-secret-extra-attr.rs"); - t.compile_fail("tests/trybuild/fail-derive-literal.rs"); + t.compile_fail("tests/trybuild/fail-forward-literal.rs"); t.compile_fail("tests/trybuild/fail-field-from-unknown-type.rs"); t.compile_fail("tests/trybuild/fail-uncreatable-type.rs"); t.compile_fail("tests/trybuild/fail-not-a-type.rs"); diff --git a/confik-macros/tests/trybuild/19-derive.rs b/confik-macros/tests/trybuild/19-derive.rs index cd3dfc0..a5c91f2 100644 --- a/confik-macros/tests/trybuild/19-derive.rs +++ b/confik-macros/tests/trybuild/19-derive.rs @@ -3,7 +3,7 @@ use std::collections::{BTreeSet, HashSet}; use confik::Configuration; #[derive(Configuration)] -#[confik(derive(::std::hash::Hash, std::cmp::Ord, PartialOrd, Eq, PartialEq, Clone))] +#[confik(forward(derive(::std::hash::Hash, std::cmp::Ord, PartialOrd, Eq, PartialEq, Clone)))] struct Target { item: usize, } diff --git a/confik-macros/tests/trybuild/23-where-clause.rs b/confik-macros/tests/trybuild/23-where-clause.rs index 071f023..38b999f 100644 --- a/confik-macros/tests/trybuild/23-where-clause.rs +++ b/confik-macros/tests/trybuild/23-where-clause.rs @@ -13,7 +13,7 @@ impl MyTrait for () { } #[derive(Configuration)] -#[confik(forward_serde(bound = "C: MyTrait + DeserializeOwned"))] +#[confik(forward(serde(bound = "C: MyTrait + DeserializeOwned")))] struct Config<C> where C: MyTrait + Default + DeserializeOwned, diff --git a/confik-macros/tests/trybuild/fail-derive-literal.stderr b/confik-macros/tests/trybuild/fail-derive-literal.stderr deleted file mode 100644 index 60b7f86..0000000 --- a/confik-macros/tests/trybuild/fail-derive-literal.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: Expected a path to a derivable trait, got Lit(Lit::Str { token: "hello world" }) - --> tests/trybuild/fail-derive-literal.rs:2:17 - | -2 | #[confik(derive("hello world"))] - | ^^^^^^^^^^^^^ diff --git a/confik-macros/tests/trybuild/fail-derive-literal.rs b/confik-macros/tests/trybuild/fail-forward-literal.rs similarity index 62% rename from confik-macros/tests/trybuild/fail-derive-literal.rs rename to confik-macros/tests/trybuild/fail-forward-literal.rs index 9f37124..f25295d 100644 --- a/confik-macros/tests/trybuild/fail-derive-literal.rs +++ b/confik-macros/tests/trybuild/fail-forward-literal.rs @@ -1,5 +1,5 @@ #[derive(confik::Configuration)] -#[confik(derive("hello world"))] +#[confik(forward("hello world"))] struct A; fn main() {} diff --git a/confik-macros/tests/trybuild/fail-forward-literal.stderr b/confik-macros/tests/trybuild/fail-forward-literal.stderr new file mode 100644 index 0000000..fb6249a --- /dev/null +++ b/confik-macros/tests/trybuild/fail-forward-literal.stderr @@ -0,0 +1,17 @@ +error: expected identifier, found `"hello world"` + --> tests/trybuild/fail-forward-literal.rs:2:18 + | +2 | #[confik(forward("hello world"))] + | ^^^^^^^^^^^^^ expected identifier + +error: proc-macro derive produced unparsable tokens + --> tests/trybuild/fail-forward-literal.rs:1:10 + | +1 | #[derive(confik::Configuration)] + | ^^^^^^^^^^^^^^^^^^^^^ + +error[E0412]: cannot find type `AConfigBuilder` in this scope + --> tests/trybuild/fail-forward-literal.rs:3:8 + | +3 | struct A; + | ^ not found in this scope diff --git a/confik/CHANGELOG.md b/confik/CHANGELOG.md index 92e551d..db00f07 100644 --- a/confik/CHANGELOG.md +++ b/confik/CHANGELOG.md @@ -12,6 +12,12 @@ num: usize, } ``` + - Replaces `confik(derive(...))`. E.g. + ```rust + #[derive(Configuration)] + #[confik(forward(derive(Hash)))] + struct Config(usize); + ``` ## 0.13.0 diff --git a/confik/examples/derives.rs b/confik/examples/derives.rs index fa70b62..ccf1f39 100644 --- a/confik/examples/derives.rs +++ b/confik/examples/derives.rs @@ -9,7 +9,7 @@ struct Config { } #[derive(Debug, Configuration, Hash, Eq, PartialEq)] -#[confik(derive(Hash, Eq, PartialEq))] +#[confik(forward(derive(Hash, Eq, PartialEq)))] struct Value { inner: String, } diff --git a/confik/src/common.rs b/confik/src/common.rs index 17666be..2835c80 100644 --- a/confik/src/common.rs +++ b/confik/src/common.rs @@ -6,7 +6,7 @@ use crate::{Configuration, MissingValue}; /// The database type, used to determine the connection string format #[derive(Debug, Clone, PartialEq, Eq, Configuration)] -#[confik(forward_serde(rename_all = "lowercase"))] +#[confik(forward(serde(rename_all = "lowercase")))] enum DatabaseKind { Mysql, Postgres, diff --git a/confik/src/lib.md b/confik/src/lib.md index 21806bd..b2c60d3 100644 --- a/confik/src/lib.md +++ b/confik/src/lib.md @@ -69,7 +69,7 @@ If a secret is found in an insecure source, an error will be returned. You can o The derive macro is called `Configuration` and is used as normal: -``` +```rust #[derive(confik::Configuration)] struct Config { data: usize, @@ -78,19 +78,37 @@ struct Config { ### Forwarding Attributes -The serde attributes used for customizing a `Deserialize` derive typically are achieved by adding `#[confik(forward(serde(...)))]` attributes. +This allows forwarding any kind of attribute on to the builder. + +#### Serde + +The serde attributes used for customizing a `Deserialize` derive are achieved by adding `#[confik(forward(serde(...)))]` attributes. For example: +```rust +# use confik::Configuration; +#[derive(Configuration, Debug, PartialEq, Eq)] +struct Field { + #[confik(forward(serde(rename = "other_name")))] + field1: usize, +} ``` -#[derive(confik::Configuration)] -struct Config { - #[confik(forward(serde(rename = "other_data")))] - data: usize, +#### Derives + +If you need additional derives for your type, these can be added via `#[confik(forward(derive...))]` attributes. + +For example: + +```rust +# use confik::Configuration; +#[derive(Debug, Configuration, Hash, Eq, PartialEq)] +#[confik(forward(derive(Hash, Eq, PartialEq)))] +struct Value { + inner: String, } ``` -This can also be used for non-serde attributes, but this is less commonly needed. ### Defaults @@ -98,7 +116,7 @@ Defaults are specified on a per-field basis. - Defaults only apply if no data has been read for that field. E.g., if `data` in the below example has one value read in, it will return an error. - ``` + ```rust # #[cfg(feature = "toml")] # { use confik::{Configuration, TomlSource}; @@ -150,7 +168,7 @@ Defaults are specified on a per-field basis. - Defaults can be given by any rust expression, and have [`Into::into`] run over them. E.g., - ``` + ```rust const DEFAULT_VALUE: u8 = 4; #[derive(confik::Configuration)] @@ -166,7 +184,7 @@ Defaults are specified on a per-field basis. - Alternatively, a default without a given value called [`Default::default`]. E.g., - ``` + ```rust use confik::{Configuration}; #[derive(Configuration)] @@ -195,7 +213,7 @@ This crate provides implementations of [`Configuration`] for a number of `std` t If there's another foreign type used in your config, then you will not be able to implement [`Configuration`] for it. Instead any type that implements [`Into`] or [`TryInto`] can be used. -``` +```rust struct ForeignType { data: usize, } diff --git a/confik/tests/keyed_containers/mod.rs b/confik/tests/keyed_containers/mod.rs index d99752e..0bd21b6 100644 --- a/confik/tests/keyed_containers/mod.rs +++ b/confik/tests/keyed_containers/mod.rs @@ -3,7 +3,7 @@ macro_rules! create_tests_for { use confik::Configuration; #[derive(Debug, Configuration, PartialEq, Eq, Hash, Ord, PartialOrd)] - #[confik(derive(Hash, PartialEq, Eq, Ord, PartialOrd))] + #[confik(forward(derive(Hash, PartialEq, Eq, Ord, PartialOrd)))] struct TwoVals { first: usize, second: usize, diff --git a/confik/tests/unkeyed_containers/mod.rs b/confik/tests/unkeyed_containers/mod.rs index d30cf06..b6758a1 100644 --- a/confik/tests/unkeyed_containers/mod.rs +++ b/confik/tests/unkeyed_containers/mod.rs @@ -3,7 +3,7 @@ macro_rules! create_tests_for { use confik::Configuration; #[derive(Debug, Configuration, PartialEq, Eq, Hash, Ord, PartialOrd)] - #[confik(derive(Hash, PartialEq, Eq, Ord, PartialOrd))] + #[confik(forward(derive(Hash, PartialEq, Eq, Ord, PartialOrd)))] struct TwoVals { first: usize, second: usize, From c6f29828ba0526330e8ae3df50c4f43e424a4ba1 Mon Sep 17 00:00:00 2001 From: tenuous-guidance <105654822+tenuous-guidance@users.noreply.github.com> Date: Wed, 9 Apr 2025 12:54:49 +0100 Subject: [PATCH 3/3] feat(derive): allow naming the builder and setting field visibility --- confik-macros/src/lib.rs | 53 +++++++++++++++---- confik-macros/tests/trybuild.rs | 6 ++- .../tests/trybuild/22-dataless-types.rs | 2 +- ...m-untagged.rs => 25-pass-enum-untagged.rs} | 0 .../tests/trybuild/26-named-builder.rs | 15 ++++++ .../tests/trybuild/27-field-access.rs | 16 ++++++ confik-macros/tests/trybuild/28-field-vis.rs | 19 +++++++ .../tests/trybuild/29-named-field-vis.rs | 18 +++++++ confik/CHANGELOG.md | 7 +++ confik/src/lib.md | 32 +++++++++++ 10 files changed, 155 insertions(+), 13 deletions(-) rename confik-macros/tests/trybuild/{pass-enum-untagged.rs => 25-pass-enum-untagged.rs} (100%) create mode 100644 confik-macros/tests/trybuild/26-named-builder.rs create mode 100644 confik-macros/tests/trybuild/27-field-access.rs create mode 100644 confik-macros/tests/trybuild/28-field-vis.rs create mode 100644 confik-macros/tests/trybuild/29-named-field-vis.rs diff --git a/confik-macros/src/lib.rs b/confik-macros/src/lib.rs index 02047a4..02f7db6 100644 --- a/confik-macros/src/lib.rs +++ b/confik-macros/src/lib.rs @@ -330,6 +330,9 @@ struct FieldImplementer { /// The field type. ty: Type, + /// `pub`, `pub(crate)`, etc. + vis: Visibility, + /// Optional attributes to forward to the builder's field. forward: Option<Forward>, } @@ -383,6 +386,7 @@ impl FieldImplementer { forward, from, try_from, + vis, .. } = field_impl.as_ref(); @@ -415,7 +419,7 @@ impl FieldImplementer { Ok(quote_spanned! { ident.span() => #[serde(default)] #forward - #ident #ty + #vis #ident #ty }) } @@ -628,6 +632,11 @@ struct RootImplementer { /// This can be serde attributes e.g. `#[confik(forward(serde(default)))]` but also others like /// `#[confik(forward(derive(Hash)))]` forward: Option<Forward>, + + /// A name to use for the builder. + /// + /// Setting this also puts the builder in the local module, so that the name is accessible. + name: Option<Ident>, } impl RootImplementer { @@ -655,7 +664,11 @@ impl RootImplementer { /// /// Use [`Self::is_dataless`] first to determine whether a builder will exist. fn builder_name(&self) -> Ident { - format_ident!("{}ConfigBuilder", self.ident) + if let Some(name) = self.name.as_ref() { + name.clone() + } else { + format_ident!("{}ConfigBuilder", self.ident) + } } /// Defines the builder for the target. @@ -920,18 +933,36 @@ fn derive_macro_builder_inner(target_struct: &DeriveInput) -> syn::Result<proc_m )] }; - let full_derive = quote! { - #overall_lint_overrides - const _: () = { - #impl_lint_overrides - #target_impl - + let full_derive = if implementer.name.is_some() { + quote! { + #overall_lint_overrides #struct_lint_overrides #builder_struct - #impl_lint_overrides - #builder_impl - }; + #overall_lint_overrides + const _: () = { + #impl_lint_overrides + #target_impl + + #overall_lint_overrides + #impl_lint_overrides + #builder_impl + }; + } + } else { + quote! { + #overall_lint_overrides + const _: () = { + #impl_lint_overrides + #target_impl + + #struct_lint_overrides + #builder_struct + + #impl_lint_overrides + #builder_impl + }; + } }; Ok(full_derive.into()) diff --git a/confik-macros/tests/trybuild.rs b/confik-macros/tests/trybuild.rs index d89c8cd..6268564 100644 --- a/confik-macros/tests/trybuild.rs +++ b/confik-macros/tests/trybuild.rs @@ -28,7 +28,11 @@ fn compile_macros() { t.pass("tests/trybuild/22-dataless-types.rs"); t.pass("tests/trybuild/23-where-clause.rs"); t.pass("tests/trybuild/24-field-try-from.rs"); - t.pass("tests/trybuild/pass-enum-untagged.rs"); + t.pass("tests/trybuild/25-pass-enum-untagged.rs"); + t.pass("tests/trybuild/26-named-builder.rs"); + t.pass("tests/trybuild/27-field-access.rs"); + t.pass("tests/trybuild/28-field-vis.rs"); + t.pass("tests/trybuild/29-named-field-vis.rs"); t.compile_fail("tests/trybuild/fail-default-parse.rs"); t.compile_fail("tests/trybuild/fail-default-invalid-expr.rs"); diff --git a/confik-macros/tests/trybuild/22-dataless-types.rs b/confik-macros/tests/trybuild/22-dataless-types.rs index d4578d2..47536d6 100644 --- a/confik-macros/tests/trybuild/22-dataless-types.rs +++ b/confik-macros/tests/trybuild/22-dataless-types.rs @@ -8,7 +8,7 @@ struct A; struct B {} #[derive(Configuration, Debug)] -struct C (); +struct C(); fn main() { let _builder = A::builder().try_build().expect("No data required"); diff --git a/confik-macros/tests/trybuild/pass-enum-untagged.rs b/confik-macros/tests/trybuild/25-pass-enum-untagged.rs similarity index 100% rename from confik-macros/tests/trybuild/pass-enum-untagged.rs rename to confik-macros/tests/trybuild/25-pass-enum-untagged.rs diff --git a/confik-macros/tests/trybuild/26-named-builder.rs b/confik-macros/tests/trybuild/26-named-builder.rs new file mode 100644 index 0000000..74e199e --- /dev/null +++ b/confik-macros/tests/trybuild/26-named-builder.rs @@ -0,0 +1,15 @@ +//! Check that we can name and reference a builder. +use confik::ConfigurationBuilder; + +#[derive(confik::Configuration, Debug, PartialEq)] +#[confik(name = C)] +struct Config { + #[confik(default)] + param: String, +} + +fn main() { + let Config { .. } = C::default() + .try_build() + .expect("Default builder should succeed"); +} diff --git a/confik-macros/tests/trybuild/27-field-access.rs b/confik-macros/tests/trybuild/27-field-access.rs new file mode 100644 index 0000000..8656385 --- /dev/null +++ b/confik-macros/tests/trybuild/27-field-access.rs @@ -0,0 +1,16 @@ +//! Check that we can reference builder fields +use confik::Configuration; + +#[derive(Configuration, Debug, PartialEq)] +struct Config { + #[confik(default)] + param: String, +} + +type Builder = <Config as Configuration>::Builder; + +fn main() { + let _ = Builder { + param: Default::default(), + }; +} diff --git a/confik-macros/tests/trybuild/28-field-vis.rs b/confik-macros/tests/trybuild/28-field-vis.rs new file mode 100644 index 0000000..f185a2b --- /dev/null +++ b/confik-macros/tests/trybuild/28-field-vis.rs @@ -0,0 +1,19 @@ +//! Check that we can reference builder fields in a different module, when they're public + +pub mod config { + use confik::Configuration; + + #[derive(Configuration, Debug, PartialEq)] + pub struct Config { + #[confik(default)] + pub param: String, + } + + pub type Builder = <Config as Configuration>::Builder; +} + +fn main() { + let _ = config::Builder { + param: Default::default(), + }; +} diff --git a/confik-macros/tests/trybuild/29-named-field-vis.rs b/confik-macros/tests/trybuild/29-named-field-vis.rs new file mode 100644 index 0000000..f0e6ad8 --- /dev/null +++ b/confik-macros/tests/trybuild/29-named-field-vis.rs @@ -0,0 +1,18 @@ +//! Check that we can reference builder fields in a different module, when they're public, using the builder's name + +pub mod config { + use confik::Configuration; + + #[derive(Configuration, Debug, PartialEq)] + #[confik(name = Builder)] + pub struct Config { + #[confik(default)] + pub param: String, + } +} + +fn main() { + let _ = config::Builder { + param: Default::default(), + }; +} diff --git a/confik/CHANGELOG.md b/confik/CHANGELOG.md index db00f07..28825df 100644 --- a/confik/CHANGELOG.md +++ b/confik/CHANGELOG.md @@ -18,6 +18,13 @@ #[confik(forward(derive(Hash)))] struct Config(usize); ``` +- Add a new `confik(name = ...)` attribute, that provides a custom name for the `Configuration::Builder` `struct` or `enum`. + - This will also place the builder in the local module, so that its name is in a known location + ```rust + #[derive(Configuration)] + #[confik(name = Builder)] + struct Config {} + ``` ## 0.13.0 diff --git a/confik/src/lib.md b/confik/src/lib.md index b2c60d3..770d1d5 100644 --- a/confik/src/lib.md +++ b/confik/src/lib.md @@ -256,6 +256,38 @@ struct Config { } ``` +### Named builders + +If you want to directly access the builders, you can provide them with a name. This will also place the builder in the local module, to ensure there's a known path with which to reference them. + +```rust +#[derive(confik::Configuration)] +#[confik(name = Builder)] +struct Config { + data: usize, +} + +let _ = Builder { data: Default::default() }; +``` + +### Field and Builder visibility + +Field and builder visibility are direclty inherited from the underlying type. E.g. + +```rust +mod config { + #[derive(confik::Configuration)] + pub struct Config { + pub data: usize, + } +} + +// Required as you can't use this syntax for struct initialisation. +type Builder = <config::Config as confik::Configuration>::Builder; + +let _ = Builder { data: Default::default() }; +``` + ## Macro Limitations ### Custom `Deserialize` Implementations