Skip to content

Commit b4f6fbd

Browse files
feat(derive): allow naming the builder and setting field visibility
1 parent 5a7ffe0 commit b4f6fbd

File tree

6 files changed

+87
-79
lines changed

6 files changed

+87
-79
lines changed

confik-macros/src/lib.rs

+42-11
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,9 @@ struct FieldImplementer {
330330
/// The field type.
331331
ty: Type,
332332

333+
/// `pub`, `pub(crate)`, etc.
334+
vis: Visibility,
335+
333336
/// Optional attributes to forward to the builder's field.
334337
forward: Option<Forward>,
335338
}
@@ -383,6 +386,7 @@ impl FieldImplementer {
383386
forward,
384387
from,
385388
try_from,
389+
vis,
386390
..
387391
} = field_impl.as_ref();
388392

@@ -415,7 +419,7 @@ impl FieldImplementer {
415419
Ok(quote_spanned! { ident.span() =>
416420
#[serde(default)]
417421
#forward
418-
#ident #ty
422+
#vis #ident #ty
419423
})
420424
}
421425

@@ -628,6 +632,11 @@ struct RootImplementer {
628632
/// This can be serde attributes e.g. `#[confik(forward(serde(default)))]` but also others like
629633
/// `#[confik(forward(derive(Hash)))]`
630634
forward: Option<Forward>,
635+
636+
/// A name to use for the builder.
637+
///
638+
/// Setting this also puts the builder in the local module, so that the name is accessible.
639+
name: Option<Ident>,
631640
}
632641

633642
impl RootImplementer {
@@ -655,7 +664,11 @@ impl RootImplementer {
655664
///
656665
/// Use [`Self::is_dataless`] first to determine whether a builder will exist.
657666
fn builder_name(&self) -> Ident {
658-
format_ident!("{}ConfigBuilder", self.ident)
667+
if let Some(name) = self.name.as_ref() {
668+
name.clone()
669+
} else {
670+
format_ident!("{}ConfigBuilder", self.ident)
671+
}
659672
}
660673

661674
/// Defines the builder for the target.
@@ -920,18 +933,36 @@ fn derive_macro_builder_inner(target_struct: &DeriveInput) -> syn::Result<proc_m
920933
)]
921934
};
922935

923-
let full_derive = quote! {
924-
#overall_lint_overrides
925-
const _: () = {
926-
#impl_lint_overrides
927-
#target_impl
928-
936+
let full_derive = if implementer.name.is_some() {
937+
quote! {
938+
#overall_lint_overrides
929939
#struct_lint_overrides
930940
#builder_struct
931941

932-
#impl_lint_overrides
933-
#builder_impl
934-
};
942+
#overall_lint_overrides
943+
const _: () = {
944+
#impl_lint_overrides
945+
#target_impl
946+
947+
#overall_lint_overrides
948+
#impl_lint_overrides
949+
#builder_impl
950+
};
951+
}
952+
} else {
953+
quote! {
954+
#overall_lint_overrides
955+
const _: () = {
956+
#impl_lint_overrides
957+
#target_impl
958+
959+
#struct_lint_overrides
960+
#builder_struct
961+
962+
#impl_lint_overrides
963+
#builder_impl
964+
};
965+
}
935966
};
936967

937968
Ok(full_derive.into())

confik-macros/tests/trybuild.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,11 @@ fn compile_macros() {
2828
t.pass("tests/trybuild/22-dataless-types.rs");
2929
t.pass("tests/trybuild/23-where-clause.rs");
3030
t.pass("tests/trybuild/24-field-try-from.rs");
31-
t.pass("tests/trybuild/pass-enum-untagged.rs");
31+
t.pass("tests/trybuild/25-pass-enum-untagged.rs");
32+
t.pass("tests/trybuild/26-named-builder.rs");
33+
t.pass("tests/trybuild/27-field-access.rs");
34+
t.pass("tests/trybuild/28-field-vis.rs");
35+
t.pass("tests/trybuild/29-named-field-vis.rs");
3236

3337
t.compile_fail("tests/trybuild/fail-default-parse.rs");
3438
t.compile_fail("tests/trybuild/fail-default-invalid-expr.rs");

confik-macros/tests/trybuild/22-dataless-types.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ struct A;
88
struct B {}
99

1010
#[derive(Configuration, Debug)]
11-
struct C ();
11+
struct C();
1212

1313
fn main() {
1414
let _builder = A::builder().try_build().expect("No data required");

confik-macros/tests/trybuild/pass-enum-untagged.rs

-66
This file was deleted.

confik/CHANGELOG.md

+7
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@
1818
#[confik(forward(derive(Hash)))]
1919
struct Config(usize);
2020
```
21+
- Add a new `confik(name = ...)` attribute, that provides a custom name for the `Configuration::Builder` `struct` or `enum`.
22+
- This will also place the builder in the local module, so that its name is in a known location
23+
```rust
24+
#[derive(Configuration)]
25+
#[confik(name = Builder)]
26+
struct Config {}
27+
```
2128

2229
## 0.13.0
2330

confik/src/lib.md

+32
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,38 @@ struct Config {
256256
}
257257
```
258258

259+
### Named builders
260+
261+
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.
262+
263+
```rust
264+
#[derive(confik::Configuration)]
265+
#[confik(name = Builder)]
266+
struct Config {
267+
data: usize,
268+
}
269+
270+
let _ = Builder { data: Default::default() };
271+
```
272+
273+
### Field and Builder visibility
274+
275+
Field and builder visibility are direclty inherited from the underlying type. E.g.
276+
277+
```rust
278+
mod config {
279+
#[derive(confik::Configuration)]
280+
pub struct Config {
281+
pub data: usize,
282+
}
283+
}
284+
285+
// Required as you can't use this syntax for struct initialisation.
286+
type Builder = <config::Config as confik::Configuration>::Builder;
287+
288+
let _ = Builder { data: Default::default() };
289+
```
290+
259291
## Macro Limitations
260292

261293
### Custom `Deserialize` Implementations

0 commit comments

Comments
 (0)