rust-typed-builder icon indicating copy to clipboard operation
rust-typed-builder copied to clipboard

Special Setter/Initializer

Open taooceros opened this issue 2 months ago • 2 comments

Is it possible to customize a setter for initializing a couple of fields? For example, I want the builder being able to accept a different struct that contains some information about some fields.

This is possible with mutator, but that will get rid of the compile time checking of those fields, which I do want the builder to be able to detect whether that setter is called.

Thanks for the very cool project!

taooceros avatar Apr 23 '24 22:04 taooceros

How would it looks like? Something like this?

#[derive(TypedBuilder)]
#[builder(setters(
        #[setter(sets = (bar, baz))]
        fn bar_and_baz(&self, bar: i32, baz: i32) -> (i32, i32) {
            (bar, baz)
        }
))]
struct Foo {
    bar: i32,
    baz: i32,
}

idanarye avatar Apr 25 '24 15:04 idanarye

This is possible outside of macros provided by typed-builder, which means we have to write some hacky generic. But it's fine with the help of cargo expand subcommand.

Say we have the following struct:

#[derive(TypedBuilder)]
struct Foo {
    bar: i32,
    baz: f64,
    qux: i16
}

With cargo expand, we can see the generated builder and helper by typed-builder. Here is the generated builder function for qux field:

#[allow(dead_code, non_camel_case_types, missing_docs)]
#[automatically_derived]
impl<__bar, __baz> FooBuilder<(__bar, __baz, ())> {
    #[allow(clippy::used_underscore_binding, clippy::no_effect_underscore_binding)]
    pub fn qux(self, qux: i16) -> FooBuilder<(__bar, __baz, (i16,))> {
        let qux = (qux,);
        let (bar, baz, ()) = self.fields;
        FooBuilder {
            fields: (bar, baz, qux),
            phantom: self.phantom,
        }
    }
}

So we can create our own functions, based on the above code from cargo expand. Here is a hand written setter for setting bar and baz at once:

#[allow(dead_code, non_camel_case_types, missing_docs)]
#[automatically_derived]
impl<__qux> FooBuilder<((), (), __qux)> {
    #[allow(clippy::used_underscore_binding, clippy::no_effect_underscore_binding)]
    // function signature is fully customizible, like function name, params.
    pub fn bar_and_baz(self, bar:i32, baz: f64) -> FooBuilder<((i32,), (f64,), __qux)> {
        let baz = (baz,);
        let bar = (bar,)
        let ((), (), qux) = self.fields;
        FooBuilder {
            fields: (bar, baz, qux),
            phantom: self.phantom,
        }
    }
}

fMeow avatar Apr 28 '24 06:04 fMeow