Add support for positional params in start_fn and finish_fn#125
Add support for positional params in start_fn and finish_fn#125
start_fn and finish_fn#125Conversation
|
cc @EdJoPaTo, @dzmitry-lahoda, and @ThomasAlban. I'd be glad to hear your feedback on this design, maybe you'd have any ideas for some better syntax to express this. For example, I'm thinking that maybe there should be some short syntax like |
8b25f3e to
65bdc95
Compare
|
First of all, thank you very much for all your work on this crate! #[builder(start_fn)]
#[builder(finish_fn)]over what is currently available. However, supplying fields as args to finish_fn is something I can't see myself ever using, I was more hoping for a single toggle at the top of the struct or fn which toggles every required field to be an arg to the start function. I was also hoping this would mean there would be no need for the finish_fn, further reducing the verbosity of the API. |
Yeah, that's why this PR "partially addresses #24" (as written at the top of the PR description)
I do think it's useful. For example an API for sending messages from one person to another #[derive(bon::Builder)]
#[builder(start_fn = from, finish_fn = to, on(String, into))]
struct Message {
#[builder(pos = start_fn)]
sender: String,
body: String,
is_urgent: bool,
#[builder(pos = finish_fn)]
recipient: String,
}
let message = Message::from("Blackjack")
.body("Hello, Bon!")
.is_urgent(true)
.to("Bon");This basically forces the developer into a specific order of method calls. In this case it requires you to specify the sender first first, and the recipient as the last field. Another use case is the API similar to sqlx: let account = sqlx::query!("select (1) as id, 'Herp Derpinson' as name")
.map(|record| (record.id, record.name))
.fetch_one(&mut conn)
.await?;Here you need to pass the DB connection object as an argument of the finishing method (i.e. where you'd like to fetch from). |
|
Ah yes I hadn't considered how it allows you to force an order in your API. Yeah, makes perfect sense and I retract my previous statement! |
Not yet, I don't know what that syntax would look like yet, and if it will be used that often to justify such additional syntax. I'll keep it simple initially |
Now I think this notation isn't obvious enough. Maybe some of these #[builder(via = start_fn)]
#[builder(via = finish_fn)]#[builder(syntax = start_fn)]
#[builder(syntax = finish_fn)]#[builder(source = start_fn)]
#[builder(source = finish_fn)]#[builder(start_fn_arg)]
#[builder(finish_fn_arg)]#[builder(positional)]
#[builder(positional(finish_fn))]#[builder(starter)]
#[builder(finisher)]#[builder(start_fn(arg))]
#[builder(finish_fn(arg))] |
|
I don't really see a nice syntax for this. The one that sucks less I think is #[builder(source = start_fn)]
#[builder(source = finish_fn)]Which delivers the semantic meaning of "where the value for this member comes from". |
|
Could do 'src' instead of 'source' to make it more concise? |
|
Yeah, I just can't decide. Now that you put it that way, it seems logical. Glad I can put the blame of naming on someone else 🐱 . Be it |
|
I think I prefer The key=value doesn’t seem to add much helpful context, might suggest more values and is longer so I think having only the keyword is a good choice. I like thinking about it with multiple people to get different views and thoughts on it! |
There is already builder(on(...)) attribute where "on" has a bit different meaning (similar to "if" or "when") So if adding more clarity, I guess |
|
|
|
Why is saving characters a benefit? Autocompletion exists, looking these up and copying will also be done quite often. The output binary size or performance will not be different in any way. We do not need to save characters. The focus should be on readable code, especially when people read code without being familiar with bon. Thinking about it again, |
Just pure aesthetics.
Unfortunately, not for proc macros. I experimented with some hacks for autocompletion, and they do work, but they don't work at member level (yet), unfortunately. I'll try to fight with that once there is nothing more in priority for |
|
Anyway, all of us are biased because we already know how this feature works. In general, I don't think |
|
Done this change was released in a 2.3 version of bon: |
|
This was merged a while ago, but I actually had to find this to see if positional arguments worked for functions too. It would be great if that example was added to the docs also |
|
@RGBCube, hi! Thanks for pointing this out. I'm all for improving the docs. Currently, the guide in
And examples in the attributes reference have different "tabs" for different syntax.
Basically almost all attributes work on all variants of syntax (structs, functions, methods), and I wanted to keep docs shorter and avoid repetitions, so I decided to go with the "tabs" approach in example snippets but only in attributes reference. There could definitely be tabs in the guide section as well, I was just lazy about doing them there because all of them are manually written =) Do you think that would fix it? |
|
Oh, I completely missed that! I was trying to get it done quick and was only skimming the guide itself 😅 I think adding this into the guide would be helpful though, it covers basically everything except this |
|
That's alright, I added syntax variations with tabs to the examples #315. The docs have been updated |

Partially addresses #24.
It's now possible to add
#[builder(start_fn/finish_fn)]to the members to mark them as positional arguments to thestart_fn/finish_fnrespectively.The relative order of members with
#[builder(start_fn/finish_fn)]matters. Such that all members withstart_fnwill go in the same order as they are declared relative to each other in the start function, and all members withfinish_fnwill go in the same order as they are declared relative to each other in the finishing method.For anyone willing to test this feature right from the oven, use this in
Cargo.toml:Here are some tests that showcase the API:
bon/bon/tests/integration/builder/positional_members.rs
Lines 19 to 145 in be3c982
This is still a draft that I'm planning to finish by the end of the week. Remaining work: