Commit f28262c
committed
Merge #82: Eliminate direct console output bypassing UserOutput abstraction
c9e6f07 fix: use steps() method with multi-line strings for numbered list (copilot-swe-agent[bot])
6a2902d fix: use info_block for numbered list with sub-items (copilot-swe-agent[bot])
3728e8f feat: eliminate direct console output bypassing UserOutput (copilot-swe-agent[bot])
6309517 Initial plan (copilot-swe-agent[bot])
Pull request description:
The presentation layer directly called `println!()` and `eprintln!()`, bypassing the `UserOutput` service abstraction. This violated the architecture, prevented verbosity control, and made output untestable.
## Changes
**Extended UserOutput API** with three new methods:
- `blank_line()` - spacing between output sections
- `steps()` - automatic numbering for sequential instructions
- `info_block()` - multi-line information blocks
All methods respect verbosity levels and route output to appropriate channels (stderr for operational messages, stdout for results).
**Refactored presentation layer** to eliminate all direct console calls:
- `create/subcommands/template.rs` - replaced 11 `println!()` calls with `steps()` and `blank_line()`
- `commands/mod.rs` - replaced 3 `eprintln!()` calls in `handle_error()` with UserOutput methods
## Example
Before:
```rust
println!();
println!("Next steps:");
println!("1. Edit the template file...");
```
After:
```rust
output.blank_line();
output.steps("Next steps:", &[
"Edit the template file...",
"Review default values...",
]);
```
Output format preserved. All messages now respect verbosity levels and can be tested/redirected.
<!-- START COPILOT CODING AGENT SUFFIX -->
<details>
<summary>Original prompt</summary>
>
> ----
>
> *This section details on the original issue you should resolve*
>
> <issue_title>Eliminate Direct Console Output Bypassing UserOutput</issue_title>
> <issue_description>**Parent Issue**: #63
> **Type**: 🎯 Quick Win
> **Impact**: 🟢🟢🟢 High
> **Effort**: 🔵🔵 Medium
> **Priority**: P0
> **Depends On**: #66 (Proposal 3)
>
> ## Problem
>
> The presentation layer directly calls `println!()` and other console output macros, bypassing the `UserOutput` service abstraction:
>
> ```rust
> fn display_success_message(output: &mut UserOutput, output_path: &Path) {
> output.success(&format!("Configuration template generated: {}", output_path.display()));
>
> println!(); // ❌ Bypasses UserOutput
> println!("Next steps:"); // ❌ Bypasses UserOutput
> println!("1. Edit the template file..."); // ❌ Bypasses UserOutput
> // More println! calls...
> }
> ```
>
> **Why this is a problem:**
> - Violates abstraction - `UserOutput` should centralize all user-facing output
> - No verbosity control - can't suppress based on verbosity level
> - Hard to test - can't capture or verify output
> - No output redirection - can't redirect to files or alternative formats
>
> ## Proposed Solution
>
> Extend `UserOutput` with structured message methods:
>
> ```rust
> impl UserOutput {
> /// Display a multi-line information block
> pub fn info_block(&mut self, title: &str, lines: &[&str]) {
> if self.verbosity >= VerbosityLevel::Normal {
> println!();
> println!("{}", title);
> for line in lines {
> println!("{}", line);
> }
> }
> }
>
> /// Display a numbered list of steps
> pub fn steps(&mut self, title: &str, steps: &[&str]) {
> if self.verbosity >= VerbosityLevel::Normal {
> println!();
> println!("{}", title);
> for (idx, step) in steps.iter().enumerate() {
> println!("{}. {}", idx + 1, step);
> }
> }
> }
>
> /// Display a blank line (for spacing)
> pub fn blank_line(&mut self) {
> if self.verbosity >= VerbosityLevel::Normal {
> println!();
> }
> }
> }
> ```
>
> Then refactor handlers:
> ```rust
> fn display_success_message(output: &mut UserOutput, output_path: &Path) {
> output.success(&format!("Configuration template generated: {}", output_path.display()));
> output.blank_line();
> output.steps("Next steps:", &[
> "Edit the template file and replace placeholder values...",
> "Review default values...",
> "Create the environment...",
> ]);
> }
> ```
>
> ## Benefits
>
> - ✅ All output goes through `UserOutput` service
> - ✅ Consistent formatting and verbosity control
> - ✅ Testable output (can verify messages in tests)
> - ✅ Supports future output redirection (files, JSON, etc.)
> - ✅ Easy to change output format globally
> - ✅ Aligns with project architecture principles
>
> ## Implementation Checklist
>
> **Phase 1: Extend UserOutput API**
> - [ ] Add `blank_line()` method to `UserOutput`
> - [ ] Add `steps()` method for numbered lists
> - [ ] Add `info_block()` method for multi-line info blocks
> - [ ] Add tests for new methods
> - [ ] Document new methods with examples
>
> **Phase 2: Refactor template subcommand**
> - [ ] Identify all `println!()` calls in `create/subcommands/template.rs`
> - [ ] Replace with appropriate `UserOutput` methods
> - [ ] Verify output format is preserved
> - [ ] Update tests to verify output
>
> **Phase 3: Audit all presentation layer files**
> - [ ] Search `src/presentation/` for `println!()`, `print!()`, `eprintln!()`, `eprint!()`
> - [ ] Create inventory of direct output calls
> - [ ] Replace each with appropriate `UserOutput` method
> - [ ] Add new `UserOutput` methods as needed
>
> **Phase 4: Validation**
> - [ ] Run all tests to ensure output is correct
> - [ ] Manual testing with different verbosity levels
> - [ ] Verify output redirection works (if implemented)
> - [ ] Run linter and fix issues
>
> ## Acceptance Criteria
>
> - [ ] `UserOutput` has methods for all output patterns used
> - [ ] No direct `println!()`, `print!()`, `eprintln!()`, or `eprint!()` calls in `src/presentation/`
> - [ ] All output respects verbosity levels
> - [ ] Output format is preserved or improved
> - [ ] All tests pass: `cargo test presentation`
> - [ ] Pre-commit checks pass: `./scripts/pre-commit.sh`
> - [ ] Code follows project conventions
>
> ## Related Documentation
>
> - [Refactor Plan](https://github.com/torrust/torrust-tracker-deployer/blob/main/docs/refactors/plans/presentation-commands-cleanup.md)</issue_description>
>
> ## Comments on the Issue (you are @copilot in this section)
>
> <comments>
> </comments>
>
</details>
- Fixes #68
<!-- START COPILOT CODING AGENT TIPS -->
---
💬 We'd love your input! Share your thoughts on Copilot coding agent in our [2 minute survey](https://gh.io/copilot-coding-agent-survey).
ACKs for top commit:
josecelano:
ACK c9e6f07
Tree-SHA512: 44202c950a5aa344a24844dab91e6cf11803f720d85345170fb3ee84c461e2ee29097665d817aae2406fa7a709761ef00955ef85f3284ecf55954e02c31676a0File tree
3 files changed
+198
-16
lines changed- src/presentation
- commands
- create/subcommands
3 files changed
+198
-16
lines changedLines changed: 13 additions & 13 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
53 | 53 | | |
54 | 54 | | |
55 | 55 | | |
56 | | - | |
57 | | - | |
58 | | - | |
59 | | - | |
60 | | - | |
61 | | - | |
62 | | - | |
63 | | - | |
64 | | - | |
65 | | - | |
66 | | - | |
67 | | - | |
68 | | - | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
69 | 69 | | |
70 | 70 | | |
71 | 71 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
119 | 119 | | |
120 | 120 | | |
121 | 121 | | |
122 | | - | |
123 | | - | |
124 | | - | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
125 | 130 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
264 | 264 | | |
265 | 265 | | |
266 | 266 | | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
267 | 347 | | |
268 | 348 | | |
269 | 349 | | |
| |||
472 | 552 | | |
473 | 553 | | |
474 | 554 | | |
| 555 | + | |
| 556 | + | |
| 557 | + | |
| 558 | + | |
| 559 | + | |
| 560 | + | |
| 561 | + | |
| 562 | + | |
| 563 | + | |
| 564 | + | |
| 565 | + | |
| 566 | + | |
| 567 | + | |
| 568 | + | |
| 569 | + | |
| 570 | + | |
| 571 | + | |
| 572 | + | |
| 573 | + | |
| 574 | + | |
| 575 | + | |
| 576 | + | |
| 577 | + | |
| 578 | + | |
| 579 | + | |
| 580 | + | |
| 581 | + | |
| 582 | + | |
| 583 | + | |
| 584 | + | |
| 585 | + | |
| 586 | + | |
| 587 | + | |
| 588 | + | |
| 589 | + | |
| 590 | + | |
| 591 | + | |
| 592 | + | |
| 593 | + | |
| 594 | + | |
| 595 | + | |
| 596 | + | |
| 597 | + | |
| 598 | + | |
| 599 | + | |
| 600 | + | |
| 601 | + | |
| 602 | + | |
| 603 | + | |
| 604 | + | |
| 605 | + | |
| 606 | + | |
| 607 | + | |
| 608 | + | |
| 609 | + | |
| 610 | + | |
| 611 | + | |
| 612 | + | |
| 613 | + | |
| 614 | + | |
| 615 | + | |
| 616 | + | |
| 617 | + | |
| 618 | + | |
| 619 | + | |
| 620 | + | |
| 621 | + | |
| 622 | + | |
| 623 | + | |
| 624 | + | |
| 625 | + | |
| 626 | + | |
| 627 | + | |
| 628 | + | |
| 629 | + | |
| 630 | + | |
| 631 | + | |
| 632 | + | |
| 633 | + | |
| 634 | + | |
| 635 | + | |
| 636 | + | |
| 637 | + | |
| 638 | + | |
| 639 | + | |
| 640 | + | |
| 641 | + | |
| 642 | + | |
| 643 | + | |
| 644 | + | |
| 645 | + | |
| 646 | + | |
| 647 | + | |
| 648 | + | |
| 649 | + | |
| 650 | + | |
| 651 | + | |
475 | 652 | | |
0 commit comments