Add MultiBar for safe deferred progress bar configuration#787
Add MultiBar for safe deferred progress bar configuration#787jgrund wants to merge 3 commits intoconsole-rs:mainfrom
Conversation
8a4d376 to
9eea25c
Compare
|
I think the core of this idea is still good. I'm not convinced we want to make semver-compatible changes to do it, and I'm not sure I want to invest much time in reviewing a PR that it looks like is mostly LLM-generated without much human oversight. Separately, not sure if the name is right since the general concept seems applicable to other ways of building up a |
|
Hi,
Do you want to make API breaking changes instead here?
Yes, this code was mostly generated with LLM, but I designed the overall solution and am manually reviewing it / testing it / updating it etc...
Would you prefer this become a builder / default API for |
Sorry, I mistyped. I don't want to make semver-incompatible changes for this.
Okay, that seems fine. Suggest you write the PR description by hand in the future.
Yes, I think that might be better. |
Looking over this a bit, would there be any functional difference between a builder and the ProgressBar::with_* API (if we fill in the bits that don't currently have a ProgressBar::set_* equivalent)? I.E. we could have a One thing I could think of:
That keeps things semver compatible and gives a clear migration path to users. Thoughts? |
Given the prevalence of the old |
Introduces MultiBar, a builder type that captures progress bar configuration without triggering draws. This prevents the common footgun where a ProgressBar draws to stderr before being added to a MultiProgress, corrupting the terminal (see console-rs#677). MultiProgress::add/insert methods now accept both ProgressBar (backwards compatible) and MultiBar via impl Into<MultiProgressInput>.
Addresses review feedback on console-rs#787. - Rename MultiBar -> ProgressBarBuilder for broader applicability. - Add MultiProgress::{register, register_at, register_from_back, register_before, register_after}, each taking ProgressBarBuilder. - Deprecate MultiProgress::{add, insert, insert_from_back, insert_before, insert_after}, pointing at the register* equivalents. - Remove the MultiProgressInput enum (no longer needed now that the new API takes ProgressBarBuilder directly). - Split ProgressBarBuilder::materialize into a panic-safe build_unregistered that runs before a MultiState slot is reserved, preventing slot leaks on panic. - Add regression tests for with_steady_tick and the tab_width+style interaction. - Migrate examples and render tests to the new API.
|
FYI: Latest push adds |
The intra-doc link fails the `rustdoc::private_intra_doc_links` lint because `set_for_stderr` is `pub(crate)`. Drop the hyperlink and describe the behavior in prose.
| pb | ||
| } | ||
|
|
||
| fn internalize_builder( |
There was a problem hiding this comment.
Nit: let's call this internalize().
| @@ -0,0 +1,436 @@ | |||
| use std::borrow::Cow; | |||
| } | ||
|
|
||
| // ProgressStyle doesn't implement Debug, so we print all other fields | ||
| impl fmt::Debug for ProgressBarBuilder { |
There was a problem hiding this comment.
Nit: this should go below the inherent impl block.
| // ProgressStyle doesn't implement Debug, so we print all other fields | ||
| impl fmt::Debug for ProgressBarBuilder { | ||
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| f.debug_struct("ProgressBarBuilder") |
There was a problem hiding this comment.
Nit: use let Self { .. } = self; to make sure we keep track of all the fields.
| } | ||
|
|
||
| impl ProgressBarBuilder { | ||
| fn base() -> Self { |
There was a problem hiding this comment.
This should be Default impl instead (presumably it can be derived).
| let mp = MultiProgress::with_draw_target(ProgressDrawTarget::hidden()); | ||
| let pb = mp.register(ProgressBarBuilder::no_length()); | ||
| assert_eq!(pb.length(), None); | ||
| } |
There was a problem hiding this comment.
This looks like a lot of tests for a fairly shallow API wrapper. Please exercise some judgement in which tests add value/coverage.
| } | ||
|
|
||
| #[test] | ||
| fn builder_with_steady_tick() { |
There was a problem hiding this comment.
Why do we need a separate test for this?
| } | ||
|
|
||
| #[test] | ||
| fn builder_tab_width_propagates_to_style() { |
There was a problem hiding this comment.
Why do we need a separate test for this?
| /// [`MultiProgress`]. See [#677] for details. | ||
| /// | ||
| /// [#677]: https://github.com/console-rs/indicatif/issues/677 | ||
| pub fn register(&self, builder: ProgressBarBuilder) -> ProgressBar { |
There was a problem hiding this comment.
Not sure I love the register() name/prefix. How about using push() and/or insert() like the std collections types?
@chris-laplante thoughts?
There was a problem hiding this comment.
What about make, make_after, make_before, etc.? or alternatively, realize, realize_after, and so on,
There was a problem hiding this comment.
Those don't really make sense to me? The question is what relation makes sense between a MultiProgress and a ProgressBar (builder). The MultiProgress doesn't make a ProgressBar, right, nor does it really realize it?
There was a problem hiding this comment.
The thought was that a MultiProgress makes a ProgressBar from a builder. Or alternatively, a builder is 'realized' to produce the ProgressBar.
There was a problem hiding this comment.
Maybe it makes sense to use build, build_after, etc.:
let builder = ProgressBarBuilder::/* whatever */;
let pb = mp.build(builder);
// or
let pb = mp.build_after(builder, &existing_pb);There was a problem hiding this comment.
Ah, I understand what're you getting at now. I think I'd still prefer to emphasize the relation between the MultiProgress and the resulting ProgressBar over the "realization" from builder to ProgressBar.
There was a problem hiding this comment.
I see - the issue is that we already have insert and friends (https://docs.rs/indicatif/latest/indicatif/struct.MultiProgress.html#method.insert_after). For the builder flavors, what about insert_with, insert_after_with, etc.?
There was a problem hiding this comment.
Yes, I had to choose a new verb as all the others were overloaded with some other meaning (even _with has a specific connotation in this codebase).
Let me know what you land on and I'll make the change.
Summary
Implements the design from #677: introduces
MultiBar, a builder type that captures progress bar configuration without triggering any draws. This prevents the common footgun where aProgressBardraws to stderr before being added to aMultiProgress, corrupting the terminal.MultiBar— plain data struct withwith_*builder methods mirroringProgressBar. No draws until added toMultiProgress.MultiProgressInput—#[doc(hidden)]#[non_exhaustive]enum withFrom<ProgressBar>andFrom<MultiBar>, enabling backwards-compatibleimpl Into<MultiProgressInput>on allMultiProgress::add/insertmethods.MultiProgress, creates a hiddenProgressBar, applies config viawith_*(no draws), sets the remote draw target, then starts steady tick last.Before (footgun)
After (safe)
Existing code using
mp.add(ProgressBar::new(...))continues to work unchanged.🤖 Generated with Claude Code