idanarye / rust-typed-builder

Compile-time type-checked builder derive
https://crates.io/crates/typed-builder
Apache License 2.0
919 stars 52 forks source link

Calling some methods on the builder type directly without calling `.build()` #124

Open KSXGitHub opened 1 year ago

KSXGitHub commented 1 year ago

I use the builder pattern mainly to emulate named arguments. But I don't want to have to call build.

What I must do currently

DoSomething::builder()
    .foo(foo)
    .bar(bar)
    .build() // <- I want to remove this
    .run()
    .await // the `into` attribute cannot handle opaque type such as `impl Future`, which was why calling `build` is the only way

What I hope for

DoSomething::builder()
    .foo(foo)
    .bar(bar)
    .run() // this function captures DoSomethingBuilder by move
    .await
polarathene commented 5 months ago

buildstructor might work for you?

  1. You impl a constructor (new() is fronted by generated builder()), with the supported fields that you want to set.
  2. Then during usage, when you're done call the build() (the "exit" method, which has been renamed to run) to indicate that you're done supplying mandatory params and that will then call the annotated new() method with those values.
  3. In this example we create an instance of self with those method params in new() and call the structs .run() method on it which we can do since the builders own .run() is from the generated builder struct thus no conflict 👍
struct MyStruct {
    foo: usize,
    bar: usize,
}

#[buildstructor::buildstructor]
impl MyStruct {
    #[builder(exit = "run")]
    async fn new(foo: usize, bar: usize) -> bool {
        (Self { foo, bar }).run()
    }

    fn run(self) -> bool {
        self.foo == self.bar
    }
}

#[tokio::main]
async fn main() {
    let result = MyStruct::builder().foo(4).bar(2).run().await;
    assert_eq!(result, false);
}