-
-
Notifications
You must be signed in to change notification settings - Fork 23
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
Add support for positional params in start_fn
and finish_fn
#125
Conversation
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: |
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_fn
respectively.The relative order of members with
#[builder(start_fn/finish_fn)]
matters. Such that all members withstart_fn
will go in the same order as they are declared relative to each other in the start function, and all members withfinish_fn
will 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: