Skip to main content

ralph_workflow/json_parser/gemini/
parser.rs

1/// Gemini event parser
2pub struct GeminiParser {
3    colors: Colors,
4    verbosity: Verbosity,
5    /// Relative path to log file (if logging enabled)
6    log_path: Option<std::path::PathBuf>,
7    display_name: String,
8    /// Unified streaming session for state tracking
9    streaming_session: Rc<RefCell<StreamingSession>>,
10    /// Terminal mode for output formatting
11    terminal_mode: RefCell<TerminalMode>,
12    /// Track last rendered content for append-only streaming.
13    last_rendered_content: RefCell<std::collections::HashMap<String, String>>,
14    /// Whether to show streaming quality metrics
15    show_streaming_metrics: bool,
16    /// Output printer for capturing or displaying output
17    printer: SharedPrinter,
18}
19
20impl GeminiParser {
21    pub(crate) fn new(colors: Colors, verbosity: Verbosity) -> Self {
22        Self::with_printer(colors, verbosity, super::printer::shared_stdout())
23    }
24
25    /// Create a new `GeminiParser` with a custom printer.
26    pub(crate) fn with_printer(
27        colors: Colors,
28        verbosity: Verbosity,
29        printer: SharedPrinter,
30    ) -> Self {
31        let verbose_warnings = matches!(verbosity, Verbosity::Debug);
32        let streaming_session = StreamingSession::new().with_verbose_warnings(verbose_warnings);
33
34        // Use the printer's is_terminal method to validate it's connected correctly
35        let _printer_is_terminal = printer.borrow().is_terminal();
36
37        Self {
38            colors,
39            verbosity,
40            log_path: None,
41            display_name: "Gemini".to_string(),
42            streaming_session: Rc::new(RefCell::new(streaming_session)),
43            terminal_mode: RefCell::new(TerminalMode::detect()),
44            last_rendered_content: RefCell::new(std::collections::HashMap::new()),
45            show_streaming_metrics: false,
46            printer,
47        }
48    }
49
50    pub(crate) const fn with_show_streaming_metrics(mut self, show: bool) -> Self {
51        self.show_streaming_metrics = show;
52        self
53    }
54
55    pub(crate) fn with_display_name(mut self, display_name: &str) -> Self {
56        self.display_name = display_name.to_string();
57        self
58    }
59
60    pub(crate) fn with_log_file(mut self, path: &str) -> Self {
61        self.log_path = Some(std::path::PathBuf::from(path));
62        self
63    }
64
65    #[cfg(any(test, feature = "test-utils"))]
66    pub fn with_terminal_mode(self, mode: TerminalMode) -> Self {
67        *self.terminal_mode.borrow_mut() = mode;
68        self
69    }
70
71    /// Create a new parser with a test printer.
72    ///
73    /// This is the primary entry point for integration tests that need
74    /// to capture parser output for verification.
75    ///
76    /// Defaults to `TerminalMode::Full` for testing streaming behavior.
77    /// Integration tests that verify streaming output need Full mode to
78    /// see per-delta rendering (non-TTY modes suppress deltas and flush at completion).
79    #[cfg(feature = "test-utils")]
80    pub fn with_printer_for_test(
81        colors: Colors,
82        verbosity: Verbosity,
83        printer: SharedPrinter,
84    ) -> Self {
85        Self::with_printer(colors, verbosity, printer).with_terminal_mode(TerminalMode::Full)
86    }
87
88    /// Set the log file path for testing.
89    ///
90    /// This allows tests to verify log file content after parsing.
91    #[cfg(feature = "test-utils")]
92    pub fn with_log_file_for_test(mut self, path: &str) -> Self {
93        self.log_path = Some(std::path::PathBuf::from(path));
94        self
95    }
96
97    /// Parse a stream for testing purposes.
98    ///
99    /// This exposes the internal `parse_stream` method for integration tests.
100    #[cfg(feature = "test-utils")]
101    pub fn parse_stream_for_test<R: std::io::BufRead>(
102        &self,
103        reader: R,
104        workspace: &dyn crate::workspace::Workspace,
105    ) -> std::io::Result<()> {
106        self.parse_stream(reader, workspace)
107    }
108
109    /// Get a shared reference to the printer.
110    ///
111    /// This allows tests, monitoring, and other code to access the printer after parsing
112    /// to verify output content, check for duplicates, or capture output for analysis.
113    /// Only available with the `test-utils` feature.
114    #[cfg(feature = "test-utils")]
115    pub fn printer(&self) -> SharedPrinter {
116        Rc::clone(&self.printer)
117    }
118
119    /// Get streaming quality metrics from the current session.
120    ///
121    /// This provides insight into the deduplication and streaming quality of the
122    /// parsing session. Only available with the `test-utils` feature.
123    #[cfg(feature = "test-utils")]
124    pub fn streaming_metrics(&self) -> StreamingQualityMetrics {
125        self.streaming_session
126            .borrow()
127            .get_streaming_quality_metrics()
128    }
129}