Skip to main content

ralph_workflow/json_parser/printer/
traits.rs

1// Printer trait and standard implementations.
2//
3// Contains the Printable trait and StdoutPrinter/StderrPrinter.
4
5/// Trait for output destinations in parsers.
6///
7/// This trait allows parsers to write to different output destinations
8/// (stdout, stderr, or test collectors) without hardcoding the specific
9/// destination. This makes parsers testable by allowing output capture.
10pub trait Printable: std::io::Write {
11    /// Check if this printer is connected to a terminal.
12    ///
13    /// This is used to determine whether to use terminal-specific features
14    /// like colors and carriage return-based updates.
15    fn is_terminal(&self) -> bool;
16}
17
18/// Printer that writes to stdout.
19#[derive(Debug)]
20pub struct StdoutPrinter {
21    stdout: Stdout,
22    is_terminal: bool,
23}
24
25impl StdoutPrinter {
26    /// Create a new stdout printer.
27    #[must_use] 
28    pub fn new() -> Self {
29        let is_terminal = std::io::stdout().is_terminal();
30        Self {
31            stdout: std::io::stdout(),
32            is_terminal,
33        }
34    }
35}
36
37impl Default for StdoutPrinter {
38    fn default() -> Self {
39        Self::new()
40    }
41}
42
43impl std::io::Write for StdoutPrinter {
44    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
45        self.stdout.write(buf)
46    }
47
48    fn flush(&mut self) -> io::Result<()> {
49        self.stdout.flush()
50    }
51}
52
53impl Printable for StdoutPrinter {
54    fn is_terminal(&self) -> bool {
55        self.is_terminal
56    }
57}
58
59/// Printer that writes to stderr.
60#[derive(Debug)]
61#[cfg(any(test, feature = "test-utils"))]
62pub struct StderrPrinter {
63    stderr: Stderr,
64    is_terminal: bool,
65}
66
67#[cfg(any(test, feature = "test-utils"))]
68impl StderrPrinter {
69    /// Create a new stderr printer.
70    #[must_use]
71    pub fn new() -> Self {
72        let is_terminal = std::io::stderr().is_terminal();
73        Self {
74            stderr: std::io::stderr(),
75            is_terminal,
76        }
77    }
78}
79
80#[cfg(any(test, feature = "test-utils"))]
81impl Default for StderrPrinter {
82    fn default() -> Self {
83        Self::new()
84    }
85}
86
87#[cfg(any(test, feature = "test-utils"))]
88impl std::io::Write for StderrPrinter {
89    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
90        self.stderr.write(buf)
91    }
92
93    fn flush(&mut self) -> io::Result<()> {
94        self.stderr.flush()
95    }
96}
97
98#[cfg(any(test, feature = "test-utils"))]
99impl Printable for StderrPrinter {
100    fn is_terminal(&self) -> bool {
101        self.is_terminal
102    }
103}
104
105/// Shared printer reference for use in parsers.
106///
107/// This type alias represents a shared, mutable reference to a printer
108/// that can be used across parser methods.
109pub type SharedPrinter = Rc<RefCell<dyn Printable>>;
110
111/// Create a shared stdout printer.
112#[must_use] 
113pub fn shared_stdout() -> SharedPrinter {
114    Rc::new(RefCell::new(StdoutPrinter::new()))
115}