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    pub fn new() -> Self {
28        let is_terminal = std::io::stdout().is_terminal();
29        Self {
30            stdout: std::io::stdout(),
31            is_terminal,
32        }
33    }
34}
35
36impl Default for StdoutPrinter {
37    fn default() -> Self {
38        Self::new()
39    }
40}
41
42impl std::io::Write for StdoutPrinter {
43    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
44        self.stdout.write(buf)
45    }
46
47    fn flush(&mut self) -> io::Result<()> {
48        self.stdout.flush()
49    }
50}
51
52impl Printable for StdoutPrinter {
53    fn is_terminal(&self) -> bool {
54        self.is_terminal
55    }
56}
57
58/// Printer that writes to stderr.
59#[derive(Debug)]
60#[cfg(any(test, feature = "test-utils"))]
61pub struct StderrPrinter {
62    stderr: Stderr,
63    is_terminal: bool,
64}
65
66#[cfg(any(test, feature = "test-utils"))]
67impl StderrPrinter {
68    /// Create a new stderr printer.
69    pub fn new() -> Self {
70        let is_terminal = std::io::stderr().is_terminal();
71        Self {
72            stderr: std::io::stderr(),
73            is_terminal,
74        }
75    }
76}
77
78#[cfg(any(test, feature = "test-utils"))]
79impl Default for StderrPrinter {
80    fn default() -> Self {
81        Self::new()
82    }
83}
84
85#[cfg(any(test, feature = "test-utils"))]
86impl std::io::Write for StderrPrinter {
87    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
88        self.stderr.write(buf)
89    }
90
91    fn flush(&mut self) -> io::Result<()> {
92        self.stderr.flush()
93    }
94}
95
96#[cfg(any(test, feature = "test-utils"))]
97impl Printable for StderrPrinter {
98    fn is_terminal(&self) -> bool {
99        self.is_terminal
100    }
101}
102
103/// Shared printer reference for use in parsers.
104///
105/// This type alias represents a shared, mutable reference to a printer
106/// that can be used across parser methods.
107pub type SharedPrinter = Rc<RefCell<dyn Printable>>;
108
109/// Create a shared stdout printer.
110pub fn shared_stdout() -> SharedPrinter {
111    Rc::new(RefCell::new(StdoutPrinter::new()))
112}