Skip to content

Refactor user_output.rs Module to Folder Structure #149

@josecelano

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

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions