osp_cli/app/sink.rs
1//! Terminal-facing output sinks used by the host layer.
2//!
3//! This module exists so host entrypoints can emit stdout/stderr through a
4//! small abstraction that works both for the real terminal and for tests.
5//!
6//! Contract:
7//!
8//! - sinks are intentionally tiny and text-oriented
9//! - buffering, snapshotting, or process stdio forwarding belong here
10//! - higher-level rendering and message formatting belong elsewhere
11
12/// Terminal-facing output sink for stdout/stderr emission.
13///
14/// Implementors should forward or buffer the supplied text exactly as received;
15/// higher layers already handle rendering, grouping, and newline decisions.
16/// Callers may write to stdout and stderr independently and can assume that
17/// empty writes are harmless.
18///
19/// # Examples
20///
21/// ```
22/// use osp_cli::app::UiSink;
23///
24/// #[derive(Default)]
25/// struct CaptureSink {
26/// stdout: String,
27/// stderr: String,
28/// }
29///
30/// impl UiSink for CaptureSink {
31/// fn write_stdout(&mut self, text: &str) {
32/// self.stdout.push_str(text);
33/// }
34///
35/// fn write_stderr(&mut self, text: &str) {
36/// self.stderr.push_str(text);
37/// }
38/// }
39///
40/// let mut sink = CaptureSink::default();
41/// sink.write_stdout("ok");
42/// sink.write_stderr("warn");
43///
44/// assert_eq!(sink.stdout, "ok");
45/// assert_eq!(sink.stderr, "warn");
46/// ```
47pub trait UiSink {
48 /// Writes text to the sink's stdout channel.
49 fn write_stdout(&mut self, text: &str);
50
51 /// Writes text to the sink's stderr channel.
52 fn write_stderr(&mut self, text: &str);
53}
54
55/// Sink that forwards output directly to the process stdio streams.
56///
57/// Empty writes are ignored.
58#[derive(Default)]
59pub struct StdIoUiSink;
60
61impl UiSink for StdIoUiSink {
62 fn write_stdout(&mut self, text: &str) {
63 if !text.is_empty() {
64 print!("{text}");
65 }
66 }
67
68 fn write_stderr(&mut self, text: &str) {
69 if !text.is_empty() {
70 eprint!("{text}");
71 }
72 }
73}
74
75/// Sink that buffers stdout and stderr for assertions and snapshot tests.
76///
77/// # Examples
78///
79/// ```
80/// use osp_cli::app::{BufferedUiSink, UiSink};
81///
82/// let mut sink = BufferedUiSink::default();
83/// sink.write_stdout("ok");
84/// sink.write_stderr("warn");
85///
86/// assert_eq!(sink.stdout, "ok");
87/// assert_eq!(sink.stderr, "warn");
88/// ```
89#[derive(Default, Debug)]
90pub struct BufferedUiSink {
91 /// Buffered stdout content in write order.
92 pub stdout: String,
93
94 /// Buffered stderr content in write order.
95 pub stderr: String,
96}
97
98impl UiSink for BufferedUiSink {
99 fn write_stdout(&mut self, text: &str) {
100 self.stdout.push_str(text);
101 }
102
103 fn write_stderr(&mut self, text: &str) {
104 self.stderr.push_str(text);
105 }
106}