Skip to content

Eliminate direct console output bypassing UserOutput abstraction#82

Merged
josecelano merged 4 commits intomainfrom
copilot/eliminate-direct-console-output
Oct 29, 2025
Merged

Eliminate direct console output bypassing UserOutput abstraction#82
josecelano merged 4 commits intomainfrom
copilot/eliminate-direct-console-output

Conversation

Copy link
Contributor

Copilot AI commented Oct 29, 2025

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:

println!();
println!("Next steps:");
println!("1. Edit the template file...");

After:

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.

Original prompt

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:

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:

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:

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

Comments on the Issue (you are @copilot in this section)


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI self-assigned this Oct 29, 2025
Copilot AI and others added 3 commits October 29, 2025 10:30
- Add blank_line(), steps(), and info_block() methods to UserOutput
- Replace all println! calls in template.rs with UserOutput methods
- Replace all eprintln! calls in handle_error with UserOutput methods
- Add comprehensive tests for new UserOutput methods
- All output now respects verbosity levels and goes through proper channels

Co-authored-by: josecelano <[email protected]>
Use info_block() instead of steps() to maintain proper structure of
numbered list with sub-items (bullet points). This preserves the exact
original output format where main steps are numbered and sub-items are
indented with dashes.

Co-authored-by: josecelano <[email protected]>
Use steps() method as intended by passing multi-line strings for steps
that have sub-items. This provides semantic clarity and automatic
numbering while maintaining the exact original output format.

Co-authored-by: josecelano <[email protected]>
Copilot AI changed the title [WIP] Eliminate direct console output bypassing UserOutput Eliminate direct console output bypassing UserOutput abstraction Oct 29, 2025
Copilot AI requested a review from josecelano October 29, 2025 10:57
@josecelano josecelano marked this pull request as ready for review October 29, 2025 11:00
Copy link
Member

@josecelano josecelano left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK c9e6f07

@josecelano josecelano merged commit f28262c into main Oct 29, 2025
32 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Eliminate Direct Console Output Bypassing UserOutput

2 participants