Trait for a command that walks a CSS AST and emits records. Implementors define: - `Row`: the command-specific data payload - `extract()`: walk one parsed stylesheet and collect (span, row) pairs - `render_text()`: format one row for text output The framework (`run()`) handles: - Looping over input files - Parsing each to a StyleSheet - Calling `extract()` and dispatching to text or JSON output
| 100 | /// - Per-file envelopes in JSON mode (always emitted, even for empty results) |
| 101 | /// - Error handling and file headers (text mode) |
| 102 | pub trait Extract: Sized { |
| 103 | /// The command-specific payload type. |
| 104 | type Row: Serialize; |
| 105 | |
| 106 | /// Per-file context computed after parsing, before rendering. |
| 107 | type FileContext: Default; |
| 108 | |
| 109 | /// Return reference to input args. |
| 110 | fn input(&self) -> &InputArgs; |
| 111 | |
| 112 | /// Return the chosen output format. |
| 113 | fn format(&self) -> OutputFormat; |
| 114 | |
| 115 | /// Walk the stylesheet and emit (span, row) pairs into `out`. |
| 116 | fn extract<'a>(&self, stylesheet: &StyleSheet<'a>, src: &str, out: &mut Vec<(Span, Self::Row)>); |
| 117 | |
| 118 | /// Format one row for text output. |
| 119 | fn render_text(&self, ctx: &Self::FileContext, file: &str, src: &str, span: Span, row: &Self::Row, color: bool); |
| 120 | |
| 121 | /// Build the per-file context from the parsed stylesheet and source. |
| 122 | fn build_context<'a>(&self, _stylesheet: &StyleSheet<'a>, _src: &str) -> Self::FileContext { |
| 123 | Self::FileContext::default() |
| 124 | } |
| 125 | |
| 126 | /// Whether to print a filename header before each file's rows in text mode. |
| 127 | fn show_file_header(&self) -> bool { |
| 128 | true |
| 129 | } |
| 130 | |
| 131 | /// Called in text mode just before a file's rows are rendered, after the file header. |
| 132 | fn render_file_preamble(&self, _file: &str, _row_count: usize, _color: bool) {} |
| 133 | |
| 134 | /// Called in text mode when a file yields no rows. |
| 135 | fn on_no_results(&self, _file: &str) {} |
| 136 | |
| 137 | /// Try parsing source as raw content before falling back to StyleSheet. |
| 138 | /// Return true if handled. |
| 139 | fn try_content(&self, _src: &str, _bump: &Bump, _out: &mut Vec<(Span, Self::Row)>) -> bool { |
| 140 | false |
| 141 | } |
| 142 | |
| 143 | /// Parse and extract from one file. Returns Ok(rows) or Err(error_record). |
| 144 | /// `on_stylesheet` is called with the stylesheet while still in scope. |
| 145 | fn parse_and_extract_file( |
| 146 | &self, |
| 147 | file: &str, |
| 148 | src: &str, |
| 149 | bump: &Bump, |
| 150 | on_stylesheet: &mut dyn for<'a> FnMut(&StyleSheet<'a>), |
| 151 | ) -> Result<Vec<(Span, Self::Row)>, ErrorRecord> { |
| 152 | let mut rows = Vec::new(); |
| 153 | if self.try_content(src, bump, &mut rows) { |
| 154 | return Ok(rows); |
| 155 | } |
| 156 | |
| 157 | let lexer = Lexer::new(&CssAtomSet::ATOMS, src); |
| 158 | let mut parser = Parser::new(bump, src, lexer); |
| 159 | let result = parser.parse_entirely::<StyleSheet>(); |
no outgoing calls
no test coverage detected