idanarye/rust-typed-builder

Feature request: Generalize `strip_option`

SOF3 opened this issue · 1 comments

SOF3 commented

Generalize strip_option to a generic attribute accepting a method and an argument list, which the setter method would proxy to.

Example

To build a struct with a Cell, we do not want to require the user to construct the Cell themselves.

#[derive(TypedBuilder)]
struct Foo {
    #[builder(transform = Cell::new(bar: u32))]
    bar: Cell<u32>,
}

The new attribute here is (in decl macro notation) transform = $path:path( $($name:pat : $ty:ty),* ).
A builder method is generated to require the specified parameters $name* and pass them to the function in $path, whose return value is used to set the field.

An equivalent notation for strip_option is transform = ::std::option::Option::Some(field: Type).

Alternatives

Use a trait to standardize this to avoid attribute parsing Example:

trait BuilderParam<U> : Sized {
    fn convert(self) -> U;
}

Then applying #[builder(transform)] on field: Type would generate

fn field(mut self, field: impl BuilderDelegate<Type>) -> Builder<...> {
    Builder {
        field: field.convert(),
        ...self
    }
}

I can write a patch for this if you find this appropriate.

Syntax-wise, I'd rather have something like this:

#[derive(TypedBuilder)]
struct Foo {
    #[builder(transform = |bar: u32| Cell::new(bar))]
    bar: Cell<u32>,
}

It'll be easier to parse because it's legal Rust syntax, it'll be more straightforward to determine the argument, and it'll allow having multiple arguments:

struct Point {
    x: f64,
    y: f64,
}

#[derive(TypedBuilder)]
struct Foo {
    #[builder(transform = |x: f64, y: f64| Point { x, y })]
    point: Point,
}

I'm also wondering whether or not this should be under setter (#[builder(setter(transform = ...))])