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    #[must_use]
67    pub fn with_terminal_mode(self, mode: TerminalMode) -> Self {
68        *self.terminal_mode.borrow_mut() = mode;
69        self
70    }
71
72    /// Create a new parser with a test printer.
73    ///
74    /// This is the primary entry point for integration tests that need
75    /// to capture parser output for verification.
76    ///
77    /// Defaults to `TerminalMode::Full` for testing streaming behavior.
78    /// Integration tests that verify streaming output need Full mode to
79    /// see per-delta rendering (non-TTY modes suppress deltas and flush at completion).
80    #[cfg(feature = "test-utils")]
81    pub fn with_printer_for_test(
82        colors: Colors,
83        verbosity: Verbosity,
84        printer: SharedPrinter,
85    ) -> Self {
86        Self::with_printer(colors, verbosity, printer).with_terminal_mode(TerminalMode::Full)
87    }
88
89    /// Set the log file path for testing.
90    ///
91    /// This allows tests to verify log file content after parsing.
92    #[cfg(feature = "test-utils")]
93    #[must_use]
94    pub fn with_log_file_for_test(mut self, path: &str) -> Self {
95        self.log_path = Some(std::path::PathBuf::from(path));
96        self
97    }
98
99    /// Parse a stream for testing purposes.
100    ///
101    /// This exposes the internal `parse_stream` method for integration tests.
102    ///
103    /// # Errors
104    ///
105    /// Returns an error if stream parsing or file operations fail.
106    #[cfg(feature = "test-utils")]
107    pub fn parse_stream_for_test<R: std::io::BufRead>(
108        &self,
109        reader: R,
110        workspace: &dyn crate::workspace::Workspace,
111    ) -> std::io::Result<()> {
112        self.parse_stream(reader, workspace)
113    }
114
115    /// Get a shared reference to the printer.
116    ///
117    /// This allows tests, monitoring, and other code to access the printer after parsing
118    /// to verify output content, check for duplicates, or capture output for analysis.
119    /// Only available with the `test-utils` feature.
120    #[cfg(feature = "test-utils")]
121    pub fn printer(&self) -> SharedPrinter {
122        Rc::clone(&self.printer)
123    }
124
125    /// Get streaming quality metrics from the current session.
126    ///
127    /// This provides insight into the deduplication and streaming quality of the
128    /// parsing session. Only available with the `test-utils` feature.
129    #[cfg(feature = "test-utils")]
130    pub fn streaming_metrics(&self) -> StreamingQualityMetrics {
131        self.streaming_session
132            .borrow()
133            .get_streaming_quality_metrics()
134    }
135}