Skip to content

Refactor user_output.rs Module to Folder Structure #149

Description

@josecelano

Refactor user_output.rs Module to Folder Structure

Issue: #149
Parent Epic: #102 - User Output Architecture Improvements
Specification: docs/issues/149-refactor-user-output-module-to-folder-structure.md

Overview

The src/presentation/user_output.rs file has grown to 4,226 lines, making it difficult to navigate and maintain. This task refactors the monolithic file into a well-organized folder module structure with focused, cohesive submodules that follow project conventions.

This refactoring improves:

  • Discoverability: Clear separation makes it easier to find specific functionality
  • Maintainability: Smaller, focused files are easier to understand and modify
  • Testability: Tests are co-located with their code using #[cfg(test)] modules
  • Collaboration: Multiple developers can work on different aspects simultaneously
  • Code Review: Smaller, focused changes are easier to review

Goals

  • Convert user_output.rs to user_output/ folder module
  • Separate concerns into logical submodules
  • Maintain backward compatibility (no public API changes)
  • Ensure all tests pass without modification
  • Follow project module organization conventions
  • Improve code discoverability and navigation

Proposed Structure

src/presentation/user_output/
├── mod.rs                    # Public API, re-exports, module documentation
├── core.rs                   # UserOutput main struct and core impl
├── theme.rs                  # Theme struct and predefined themes
├── verbosity.rs              # VerbosityLevel enum and VerbosityFilter
├── channel.rs                # Channel enum
├── traits.rs                 # OutputMessage, FormatterOverride, OutputSink traits
├── messages/                 # Message type implementations
│   ├── mod.rs
│   ├── progress.rs           # ProgressMessage + tests
│   ├── success.rs            # SuccessMessage + tests
│   ├── warning.rs            # WarningMessage + tests
│   ├── error.rs              # ErrorMessage + tests
│   ├── result.rs             # ResultMessage + tests
│   ├── steps.rs              # StepsMessage and builder + tests
│   └── info_block.rs         # InfoBlockMessage and builder + tests
├── sinks/                    # OutputSink implementations
│   ├── mod.rs
│   ├── standard.rs           # StandardSink + tests
│   ├── composite.rs          # CompositeSink + tests
│   ├── file.rs               # FileSink + tests
│   ├── telemetry.rs          # TelemetrySink + tests
│   └── writers.rs            # StdoutWriter, StderrWriter wrappers + tests
├── formatters/               # FormatterOverride implementations
│   ├── mod.rs
│   └── json.rs               # JsonFormatter + tests
└── test_support.rs           # TestWriter, TestUserOutput (shared test utilities)

Note on Test Organization: All tests are unit tests that only depend on types within the user_output module. Following project conventions, they will be co-located with their code using #[cfg(test)] modules within each file. This improves discoverability and maintainability.

Implementation Plan

Phase 1: Create Folder Structure and Core Modules (1-2 hours)

  • Create src/presentation/user_output/ directory
  • Create mod.rs with module documentation and structure outline
  • Extract Theme to theme.rs with tests in #[cfg(test)] module
  • Extract VerbosityLevel and VerbosityFilter to verbosity.rs with tests in #[cfg(test)] module
  • Extract Channel to channel.rs with tests in #[cfg(test)] module
  • Update mod.rs with re-exports for backward compatibility
  • Verify existing code still compiles and all tests pass

Phase 2: Extract Traits and Core Logic (1-2 hours)

  • Extract OutputMessage, FormatterOverride, OutputSink to traits.rs
  • Extract main UserOutput struct and methods to core.rs
  • Update mod.rs with re-exports
  • Verify all functionality works

Phase 3: Organize Message Types (2-3 hours)

  • Create messages/ subdirectory
  • Extract each message type to its own file with tests in #[cfg(test)] modules:
    • progress.rs - ProgressMessage with tests
    • success.rs - SuccessMessage with tests
    • warning.rs - WarningMessage with tests
    • error.rs - ErrorMessage with tests
    • result.rs - ResultMessage with tests
    • steps.rs - StepsMessage, StepsMessageBuilder, and tests
    • info_block.rs - InfoBlockMessage, InfoBlockMessageBuilder, and tests
  • Create messages/mod.rs with re-exports
  • Update parent mod.rs with message re-exports
  • Verify all message tests pass

Phase 4: Organize Sink Implementations (1-2 hours)

  • Create sinks/ subdirectory
  • Extract StdoutWriter and StderrWriter to writers.rs with tests in #[cfg(test)] module
  • Extract sink implementations to individual files with tests:
    • standard.rs - StandardSink with tests
    • composite.rs - CompositeSink with tests
    • file.rs - FileSink with tests
    • telemetry.rs - TelemetrySink with tests
  • Create sinks/mod.rs with re-exports
  • Update parent mod.rs with sink re-exports
  • Verify all sink tests pass

Phase 5: Organize Formatters (30 minutes)

  • Create formatters/ subdirectory
  • Extract JsonFormatter to json.rs with tests in #[cfg(test)] module
  • Create formatters/mod.rs with re-exports
  • Update parent mod.rs with formatter re-exports
  • Verify formatter tests pass

Phase 6: Organize Test Infrastructure (30 minutes)

  • Extract test_support module to test_support.rs
  • Verify test_support is accessible for testing in other modules
  • Verify all co-located unit tests still pass
  • Check that test utilities (TestWriter, TestUserOutput) work correctly

Phase 7: Documentation and Cleanup (30 minutes)

  • Update module-level documentation in mod.rs
  • Add module documentation to each submodule
  • Update imports in mod.rs for clarity
  • Remove old user_output.rs file
  • Verify documentation builds: cargo doc --no-deps

Phase 8: Final Verification (30 minutes)

  • Run pre-commit checks: ./scripts/pre-commit.sh
  • Verify no public API changes
  • Verify all tests pass
  • Verify no unused dependencies: cargo machete
  • Check for any remaining TODO comments
  • Manual smoke test of basic functionality

Acceptance Criteria

Note for Contributors: These criteria define what the PR reviewer will check. Use this as your pre-review checklist before submitting the PR to minimize back-and-forth iterations.

Quality Checks:

  • Pre-commit checks pass: ./scripts/pre-commit.sh
  • All tests pass without modification: cargo test
  • Documentation builds successfully: cargo doc --no-deps
  • No unused dependencies: cargo machete

Structural Criteria:

  • user_output.rs is converted to user_output/ folder
  • mod.rs serves as the public API entry point
  • All submodules are appropriately sized (< 500 lines each)
  • Clear separation of concerns across submodules
  • Tests are co-located with code using #[cfg(test)] modules

Backward Compatibility:

  • All existing public types remain accessible
  • All existing import paths continue to work
  • No changes required in code using UserOutput
  • All existing tests pass without modification
  • Public API surface is identical

Code Quality:

  • Each module has clear, focused responsibility
  • Module documentation explains purpose and usage
  • Follows project module organization conventions
  • No circular dependencies between submodules
  • Private implementation details not leaked

Testing:

  • Tests organized using #[cfg(test)] modules per file
  • Test coverage maintained (no reduction)
  • Test infrastructure (test_support) easily accessible
  • All unit tests pass

Related Documentation

Estimated Time

8-10 hours total for complete refactoring with testing and documentation

Metadata

Metadata

Assignees

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions