use super::super::range::Range;
use super::super::text_content::TextContent;
use super::super::traits::{AstNode, Container, Visitor, VisualStructure};
use super::annotation::Annotation;
use super::container::GeneralContainer;
use super::content_item::ContentItem;
use super::list::List;
use super::typed_content::ContentElement;
use super::verbatim::VerbatimBlockMode;
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TableCellAlignment {
Left,
Center,
Right,
None,
}
#[derive(Debug, Clone, PartialEq)]
pub struct TableCell {
pub content: TextContent,
pub children: GeneralContainer,
pub colspan: usize,
pub rowspan: usize,
pub align: TableCellAlignment,
pub header: bool,
pub location: Range,
}
impl TableCell {
pub fn new(content: TextContent) -> Self {
Self {
content,
children: GeneralContainer::empty(),
colspan: 1,
rowspan: 1,
align: TableCellAlignment::None,
header: false,
location: Range::default(),
}
}
pub fn with_children(mut self, children: Vec<ContentElement>) -> Self {
self.children = GeneralContainer::from_typed(children);
self
}
pub fn has_block_content(&self) -> bool {
!self.children.is_empty()
}
pub fn with_span(mut self, colspan: usize, rowspan: usize) -> Self {
self.colspan = colspan;
self.rowspan = rowspan;
self
}
pub fn with_align(mut self, align: TableCellAlignment) -> Self {
self.align = align;
self
}
pub fn with_header(mut self, header: bool) -> Self {
self.header = header;
self
}
pub fn at(mut self, location: Range) -> Self {
self.location = location;
self
}
pub fn text(&self) -> &str {
self.content.as_string()
}
pub fn is_empty(&self) -> bool {
self.content.as_string().trim().is_empty()
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct TableRow {
pub cells: Vec<TableCell>,
pub location: Range,
}
impl TableRow {
pub fn new(cells: Vec<TableCell>) -> Self {
Self {
cells,
location: Range::default(),
}
}
pub fn at(mut self, location: Range) -> Self {
self.location = location;
self
}
pub fn cell_count(&self) -> usize {
self.cells.len()
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Table {
pub subject: TextContent,
pub header_rows: Vec<TableRow>,
pub body_rows: Vec<TableRow>,
pub footnotes: Option<Box<List>>,
pub annotations: Vec<Annotation>,
pub location: Range,
pub mode: VerbatimBlockMode,
}
impl Table {
pub fn new(
subject: TextContent,
header_rows: Vec<TableRow>,
body_rows: Vec<TableRow>,
mode: VerbatimBlockMode,
) -> Self {
Self {
subject,
header_rows,
body_rows,
footnotes: None,
annotations: Vec::new(),
location: Range::default(),
mode,
}
}
pub fn with_footnotes(mut self, footnotes: List) -> Self {
self.footnotes = Some(Box::new(footnotes));
self
}
pub fn at(mut self, location: Range) -> Self {
self.location = location;
self
}
pub fn all_rows(&self) -> impl Iterator<Item = &TableRow> {
self.header_rows.iter().chain(self.body_rows.iter())
}
pub fn row_count(&self) -> usize {
self.header_rows.len() + self.body_rows.len()
}
pub fn column_count(&self) -> usize {
self.all_rows()
.map(|row| row.cells.len())
.max()
.unwrap_or(0)
}
pub fn cell_children_iter(&self) -> impl Iterator<Item = &ContentItem> {
self.all_rows()
.flat_map(|row| row.cells.iter())
.flat_map(|cell| cell.children.iter())
}
pub fn annotations(&self) -> &[Annotation] {
&self.annotations
}
pub fn annotations_mut(&mut self) -> &mut Vec<Annotation> {
&mut self.annotations
}
}
impl AstNode for Table {
fn node_type(&self) -> &'static str {
"Table"
}
fn display_label(&self) -> String {
let subject_text = self.subject.as_string();
if subject_text.chars().count() > 50 {
format!("{}…", subject_text.chars().take(50).collect::<String>())
} else {
subject_text.to_string()
}
}
fn range(&self) -> &Range {
&self.location
}
fn accept(&self, visitor: &mut dyn Visitor) {
visitor.visit_table(self);
for row in self.all_rows() {
for cell in &row.cells {
for child in cell.children.iter() {
child.accept(visitor);
}
}
}
for annotation in &self.annotations {
annotation.accept(visitor);
}
if let Some(footnotes) = &self.footnotes {
footnotes.accept(visitor);
}
visitor.leave_table(self);
}
}
impl VisualStructure for Table {
fn is_source_line_node(&self) -> bool {
true
}
fn has_visual_header(&self) -> bool {
true
}
}
impl Container for Table {
fn label(&self) -> &str {
self.subject.as_string()
}
fn children(&self) -> &[ContentItem] {
&[]
}
fn children_mut(&mut self) -> &mut Vec<ContentItem> {
panic!("Tables use structured rows/cells, not generic children")
}
}
impl fmt::Display for Table {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Table('{}', {} header + {} body rows, {} cols)",
self.subject.as_string(),
self.header_rows.len(),
self.body_rows.len(),
self.column_count()
)
}
}