lex_core/lex/ast/elements/
table.rs1use super::super::range::Range;
39use super::super::text_content::TextContent;
40use super::super::traits::{AstNode, Container, Visitor, VisualStructure};
41use super::annotation::Annotation;
42use super::container::GeneralContainer;
43use super::content_item::ContentItem;
44use super::list::List;
45use super::typed_content::ContentElement;
46use super::verbatim::VerbatimBlockMode;
47use std::fmt;
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51pub enum TableCellAlignment {
52 Left,
54 Center,
56 Right,
58 None,
60}
61
62#[derive(Debug, Clone, PartialEq)]
64pub struct TableCell {
65 pub content: TextContent,
67 pub children: GeneralContainer,
69 pub colspan: usize,
71 pub rowspan: usize,
73 pub align: TableCellAlignment,
75 pub header: bool,
77 pub location: Range,
79}
80
81impl TableCell {
82 pub fn new(content: TextContent) -> Self {
83 Self {
84 content,
85 children: GeneralContainer::empty(),
86 colspan: 1,
87 rowspan: 1,
88 align: TableCellAlignment::None,
89 header: false,
90 location: Range::default(),
91 }
92 }
93
94 pub fn with_children(mut self, children: Vec<ContentElement>) -> Self {
95 self.children = GeneralContainer::from_typed(children);
96 self
97 }
98
99 pub fn has_block_content(&self) -> bool {
101 !self.children.is_empty()
102 }
103
104 pub fn with_span(mut self, colspan: usize, rowspan: usize) -> Self {
105 self.colspan = colspan;
106 self.rowspan = rowspan;
107 self
108 }
109
110 pub fn with_align(mut self, align: TableCellAlignment) -> Self {
111 self.align = align;
112 self
113 }
114
115 pub fn with_header(mut self, header: bool) -> Self {
116 self.header = header;
117 self
118 }
119
120 pub fn at(mut self, location: Range) -> Self {
121 self.location = location;
122 self
123 }
124
125 pub fn text(&self) -> &str {
127 self.content.as_string()
128 }
129
130 pub fn is_empty(&self) -> bool {
132 self.content.as_string().trim().is_empty()
133 }
134}
135
136#[derive(Debug, Clone, PartialEq)]
138pub struct TableRow {
139 pub cells: Vec<TableCell>,
141 pub location: Range,
143}
144
145impl TableRow {
146 pub fn new(cells: Vec<TableCell>) -> Self {
147 Self {
148 cells,
149 location: Range::default(),
150 }
151 }
152
153 pub fn at(mut self, location: Range) -> Self {
154 self.location = location;
155 self
156 }
157
158 pub fn cell_count(&self) -> usize {
160 self.cells.len()
161 }
162}
163
164#[derive(Debug, Clone, PartialEq)]
166pub struct Table {
167 pub subject: TextContent,
169 pub header_rows: Vec<TableRow>,
171 pub body_rows: Vec<TableRow>,
173 pub footnotes: Option<Box<List>>,
175 pub annotations: Vec<Annotation>,
177 pub location: Range,
179 pub mode: VerbatimBlockMode,
181}
182
183impl Table {
184 pub fn new(
185 subject: TextContent,
186 header_rows: Vec<TableRow>,
187 body_rows: Vec<TableRow>,
188 mode: VerbatimBlockMode,
189 ) -> Self {
190 Self {
191 subject,
192 header_rows,
193 body_rows,
194 footnotes: None,
195 annotations: Vec::new(),
196 location: Range::default(),
197 mode,
198 }
199 }
200
201 pub fn with_footnotes(mut self, footnotes: List) -> Self {
202 self.footnotes = Some(Box::new(footnotes));
203 self
204 }
205
206 pub fn at(mut self, location: Range) -> Self {
207 self.location = location;
208 self
209 }
210
211 pub fn all_rows(&self) -> impl Iterator<Item = &TableRow> {
213 self.header_rows.iter().chain(self.body_rows.iter())
214 }
215
216 pub fn row_count(&self) -> usize {
218 self.header_rows.len() + self.body_rows.len()
219 }
220
221 pub fn column_count(&self) -> usize {
223 self.all_rows()
224 .map(|row| row.cells.len())
225 .max()
226 .unwrap_or(0)
227 }
228
229 pub fn annotations(&self) -> &[Annotation] {
231 &self.annotations
232 }
233
234 pub fn annotations_mut(&mut self) -> &mut Vec<Annotation> {
236 &mut self.annotations
237 }
238}
239
240impl AstNode for Table {
241 fn node_type(&self) -> &'static str {
242 "Table"
243 }
244
245 fn display_label(&self) -> String {
246 let subject_text = self.subject.as_string();
247 if subject_text.chars().count() > 50 {
248 format!("{}…", subject_text.chars().take(50).collect::<String>())
249 } else {
250 subject_text.to_string()
251 }
252 }
253
254 fn range(&self) -> &Range {
255 &self.location
256 }
257
258 fn accept(&self, visitor: &mut dyn Visitor) {
259 visitor.visit_table(self);
260 visitor.leave_table(self);
261 }
262}
263
264impl VisualStructure for Table {
265 fn is_source_line_node(&self) -> bool {
266 true
267 }
268
269 fn has_visual_header(&self) -> bool {
270 true
271 }
272}
273
274impl Container for Table {
275 fn label(&self) -> &str {
276 self.subject.as_string()
277 }
278
279 fn children(&self) -> &[ContentItem] {
280 &[]
283 }
284
285 fn children_mut(&mut self) -> &mut Vec<ContentItem> {
286 panic!("Tables use structured rows/cells, not generic children")
290 }
291}
292
293impl fmt::Display for Table {
294 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
295 write!(
296 f,
297 "Table('{}', {} header + {} body rows, {} cols)",
298 self.subject.as_string(),
299 self.header_rows.len(),
300 self.body_rows.len(),
301 self.column_count()
302 )
303 }
304}