-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathmod.rs
More file actions
88 lines (85 loc) · 3.46 KB
/
Copy pathmod.rs
File metadata and controls
88 lines (85 loc) · 3.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
//! User-facing output handling
//!
//! This module provides user-facing output functionality separate from internal logging.
//! It implements a dual-channel strategy following Unix conventions and modern CLI best practices
//! (similar to cargo, docker, npm):
//!
//! - **stdout (Results Channel)**: Final results, structured data, output for piping/redirection
//! - **stderr (Progress/Operational Channel)**: Progress updates, status messages, warnings, errors
//!
//! This separation enables:
//! - Clean piping: `torrust-tracker-deployer destroy env | jq .status` works correctly
//! - Automation friendly: Scripts can redirect progress to /dev/null while capturing results
//! - Unix convention compliance: Follows established patterns from modern CLI tools
//! - Better UX: Progress feedback doesn't interfere with result data
//!
//! ## Type-Safe Channel Routing
//!
//! The module uses newtype wrappers (`StdoutWriter` and `StderrWriter`) to provide compile-time
//! guarantees that messages are routed to the correct output channel. This prevents accidental
//! channel confusion and makes the code more maintainable by catching routing errors at compile
//! time rather than runtime.
//!
//! The newtype pattern is a zero-cost abstraction - it has the same memory layout and performance
//! characteristics as the wrapped type, but provides type safety benefits.
//!
//! ## Buffering Behavior
//!
//! Output is line-buffered by default. Messages are typically flushed automatically
//! after each newline. For cases where immediate output is critical (e.g., before
//! long-running operations), call `flush()` explicitly:
//!
//! ```rust,ignore
//! output.progress("Starting long operation...");
//! output.flush()?; // Ensure message appears before operation starts
//! perform_long_operation();
//! ```
//!
//! ## Example Usage
//!
//! ```rust
//! use torrust_tracker_deployer_lib::presentation::user_output::{UserOutput, VerbosityLevel};
//!
//! let mut output = UserOutput::new(VerbosityLevel::Normal);
//!
//! // Progress messages go to stderr
//! output.progress("Destroying environment...");
//!
//! // Success status goes to stderr
//! output.success("Environment destroyed successfully");
//!
//! // Results go to stdout for piping
//! output.result(r#"{"status": "destroyed"}"#);
//! ```
//!
//! ## Channel Strategy
//!
//! Based on research from [`docs/research/UX/console-app-output-patterns.md`](../../docs/research/UX/console-app-output-patterns.md):
//!
//! - **stdout**: Deployment results, configuration summaries, structured data (JSON)
//! - **stderr**: Step progress, status updates, warnings, error messages with actionable guidance
//!
//! See also: [`docs/research/UX/user-output-vs-logging-separation.md`](../../docs/research/UX/user-output-vs-logging-separation.md)
// Re-export core types and traits for backward compatibility
pub use channel::Channel;
pub use core::UserOutput;
pub use formatters::JsonFormatter;
pub use messages::{
ErrorMessage, InfoBlockMessage, InfoBlockMessageBuilder, ProgressMessage, ResultMessage,
StepsMessage, StepsMessageBuilder, SuccessMessage, WarningMessage,
};
pub use sinks::{CompositeSink, FileSink, StandardSink, TelemetrySink};
pub use theme::Theme;
pub use traits::{FormatterOverride, OutputMessage, OutputSink};
pub use verbosity::VerbosityLevel;
// Internal modules
mod channel;
mod core;
mod formatters;
mod messages;
mod sinks;
mod theme;
mod traits;
mod verbosity;
// Test support module (public for use in tests across the codebase)
pub mod test_support;