Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Context for builder #75

Open
wackazong opened this issue Feb 13, 2023 · 5 comments
Open

Context for builder #75

wackazong opened this issue Feb 13, 2023 · 5 comments

Comments

@wackazong
Copy link

I would love to see a feature that allows to set a context for the builder. The context could then be used in the default functions. To keep the builder() function as is, there could be a .context() function to set the context. Then we could use something like::

#[builder(default_code = "self.context()")]

What do you think?

I find the code of this crate quite complex for me as a Rust beginner, but I would be happy to do a PR if given some pointers on where to start.

@wackazong
Copy link
Author

My use case would be to allow for the control of default values for keys and timestamps in different testing environments.

@idanarye
Copy link
Owner

To keep the builder() function as is, there could be a .context() function to set the context.

So... something like this?

#[derive(TypedBuilder)]
#[builder(context(/*Syntax TBD*/))]
struct Foo {
    #[buidler(default = self.context())]
    bar: i32,
}

// This will work:
Foo::builder().context(Context::new()).build();

// This will work:
Foo::builder().bar(42).build();

// This will not:
Foo::builder().build();

This can be a bit hard to model, but what if we require the context to always have a default?

Also, rather than a "context" method, I'd rather do something ike this:

#[derive(TypedBuilder)]
#[builder(context(
    // Will be treated like a field that does not get into the final struct
    number: i32 = 4,
))]
struct Foo {
    #[buidler(default = number * 10 + 2)]
    bar: i32,
}

@wackazong
Copy link
Author

wackazong commented Feb 13, 2023

Yes, the first proposition would be great. The context would just need to be available in the builder, not in the final struct. I would like to set the context at runtime, therefore an attribute macro as you suggested would not serve me.

Maybe like this?

struct Context;

impl Context {
    fn bar() {
        ...
    }
}

#[derive(TypedBuilder)]
struct Foo {
    #[builder(use_context_for_default)]
    bar: i32,
}

// if a field is marked with use_context_for_default then a context needs to be supplied
// and the default value is derived from the context passed in at runtime

// context will be required for each call of builder if default value is not given
Foo::builder().context(Context::new()).build();

// This will work:
Foo::builder().bar(42).build();

// This will not:
Foo::builder().build();

@idanarye
Copy link
Owner

I really don't want to move the defaults elsewhere. If I have to specify dependencies, I'd rather make a breaking change and do something like this:

#[derive(TypedBuilder)]
#[builder(context(
    x: i32 = 1,
    y: i32 = 2,
))]
struct MyStruct {
    // Depends only on `x`
    #[buidler(default = |x| x + 1)]
    foo: i32,

    // Depends only on `y`
    #[buidler(default = |y| y + 2)]
    bar: i32,

    // Depends on both
    #[buidler(default = |x, y| x + y + 3)]
    baz: i32,

    // Depends on nothing
    #[buidler(default = || 4)]
    baz: i32,
}

But... I really don't want to jump into the complexities of these dependencies, and I think that if it's just for the context fields I can get a way with using default values.

@wackazong
Copy link
Author

For me the most important would be to pass in the context at runtime and not at compile time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants