Bon with Newtype Pattern

I have a newtype wrapper pattern with my struct:

pub struct Config(Arc<ConfigInner>);

struct ConfigInner {
    setting_a: bool,
    setting_b: String,
}

I'd like to use bon to make a builder for Config, but I'm not seeing anything in the documentation that describes how to deal with a newtype-pattern struct like I'm doing here.

I've used bon for many other projects in the past, but this is my first time dealing with this.

Is there a way to configure bon to define a builder on Config which successfully wraps the inner value?

After some help from the creator (thank you!!!) I arrived at this:

pub mod config {
    use bon::Builder;
    use std::sync::Arc;

    #[derive(Clone)]
    pub struct Config(Arc<ConfigInner>);

    impl Config {
        pub fn builder() -> ConfigBuilder {
            ConfigInner::builder()
        }
    }

    #[derive(Builder)]
    #[builder(
        builder_type = ConfigBuilder,
        finish_fn(name = build_internal, vis="")
    )]
    struct ConfigInner {
        #[builder(into)]
        default_global_level: Option<String>,
        default_directives: Vec<String>,
    }

    impl<S: config_builder::IsComplete> ConfigBuilder<S> {
        pub fn build(self) -> Config {
            Config(Arc::new(self.build_internal()))
        }
    }
}

let c = Config::builder()
    .default_global_level("INFO".into())
    .default_directives(vec!["a=ERROR".to_string()])
    .build();

Unfortunately I'm getting visibility errors now:

[E0624] Error: method `default_global_level` is private
    ╭─[command_10:1:1]
    │
 18 │     #[derive(Builder)]
    │              ───┬───  
    │                 ╰───── private method defined here
    │ 
 37 │     .default_global_level("INFO".into())
    │      ──────────┬─────────  
    │                ╰─────────── private method
────╯

Arrived at the solution with the help of the maintainer, thanks again!

use bon;

pub mod config {
    use bon::Builder;
    use std::sync::Arc;

    #[derive(Clone)]
    pub struct Config(Arc<ConfigInner>);

    impl Config {
        pub fn builder() -> ConfigBuilder {
            ConfigInner::builder()
        }
    }

    #[derive(Builder)]
    #[builder(
        builder_type(vis = "pub", name = ConfigBuilder),
        finish_fn(name = build_internal, vis="")
    )]
    struct ConfigInner {
        #[builder(into)]
        default_global_level: Option<String>,
        default_directives: Vec<String>,
    }

    impl<S: config_builder::IsComplete> ConfigBuilder<S> {
        pub fn build(self) -> Config {
            Config(Arc::new(self.build_internal()))
        }
    }
}

fn main() {
    let c = config::Config::builder()
        .default_global_level("INFO")
        .default_directives(vec!["a=ERROR".to_string()])
        .build();
}
2 Likes

Bon is impressively flexible, I'm a big fan.

I bet you'd even be able to get default_directives to accept [&'str] so you could write:

let c = config::Config::builder()
        .default_global_level("INFO")
        .default_directives(["a=ERROR"])
        .build();