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.
13pub trait UiSink {
14 /// Writes text to the sink's stdout channel.
15 fn write_stdout(&mut self, text: &str);
16
17 /// Writes text to the sink's stderr channel.
18 fn write_stderr(&mut self, text: &str);
19}
20
21/// Sink that forwards output directly to the process stdio streams.
22#[derive(Default)]
23pub struct StdIoUiSink;
24
25impl UiSink for StdIoUiSink {
26 fn write_stdout(&mut self, text: &str) {
27 if !text.is_empty() {
28 print!("{text}");
29 }
30 }
31
32 fn write_stderr(&mut self, text: &str) {
33 if !text.is_empty() {
34 eprint!("{text}");
35 }
36 }
37}
38
39/// Sink that buffers stdout and stderr for assertions and snapshot tests.
40///
41/// # Examples
42///
43/// ```
44/// use osp_cli::app::{BufferedUiSink, UiSink};
45///
46/// let mut sink = BufferedUiSink::default();
47/// sink.write_stdout("ok");
48/// sink.write_stderr("warn");
49///
50/// assert_eq!(sink.stdout, "ok");
51/// assert_eq!(sink.stderr, "warn");
52/// ```
53#[derive(Default, Debug)]
54pub struct BufferedUiSink {
55 /// Buffered stdout content in write order.
56 pub stdout: String,
57
58 /// Buffered stderr content in write order.
59 pub stderr: String,
60}
61
62impl UiSink for BufferedUiSink {
63 fn write_stdout(&mut self, text: &str) {
64 self.stdout.push_str(text);
65 }
66
67 fn write_stderr(&mut self, text: &str) {
68 self.stderr.push_str(text);
69 }
70}