BrynCooke / buildstructor

Derive a builder for your constructors in Rust
Other
65 stars 4 forks source link

Special-case tuples into multiple arguments #184

Open typedrat opened 5 months ago

typedrat commented 5 months ago

Currently, if you have your constructor take a tuple argument, the builder function requires a tuple. I'm using that to represent multiple values that don't make sense to set independently, but it would be a little bit cleaner if the methods on the builder took multiple arguments, one for each member of the tuple, rather than a tuple itself.

polarathene commented 5 months ago

You can specify a separate method for your builder to use instead of new(args: (x, y ,z)).

You could then have your individual parameters as an alternative method that internally returns Self::new(args):

#[derive(Debug)]
struct Language {
    name: String,
    id: u32,
    favourite: bool,
}

#[buildstructor::buildstructor]
impl Language {
    pub fn new(args: (String, u32, bool)) -> Self {
        Self {
            name: args.0,
            id: args.1,
            favourite: args.2,
        }
    }

    // Not `pub fn`, so it's private/hidden while the builder method will be public:
    #[builder(entry = "builder", exit = "build", visibility="pub")]
    fn from_args(name: String, id: u32, favourite: bool) -> Self {
        Self::new((name, id, favourite))
    }
}

fn main() {
    let lang = Language::builder()
        .name("Rust")
        .id(42)
        .favourite(true)
        .build();

    println!("{lang:#?}");
}
$ cargo run

Language {
    name: "Rust",
    id: 42,
    favourite: true,
}

You were a bit vague on the issue and what the ideal solution would look like with buildstructor, so I may have misunderstood 😅


UPDATE: Perhaps you were requesting the builder to abstract away the need for wrapping args as a tuple type for the generated parameter method, but internally still map as a tuple?

I have raised that feature request here (it seems to be partially supported already for collections): https://github.com/BrynCooke/buildstructor/issues/187

// NOTE: Below is just an example, not actually supported yet

#[derive(Debug)]
struct Language {
    name: String,
    id: u32,
    favourite: bool,
}

#[buildstructor::buildstructor]
impl Language {
    #[builder]
    pub fn new(args: (String, u32, bool)) -> Self {
        Self {
            name: args.0,
            id: args.1,
            favourite: args.2,
        }
    }
}

fn main() {
    // Instead of tuple wrapped single-arg `.args(("Rust", 42, true))`,
    // take each as a separate arg to `.args()` instead:
    let lang = Language::builder()
        .args("Rust", 42, true)
        .build();

    println!("{lang:#?}");
}

In the linked example I have a Pair<X, Y>(X, Y) / Pair<String, String> type where having the separate values as individual args might make more sense for the feature than the example I adapted above.