math_core_renderer_internal/
table.rs1use std::{fmt::Write, num::NonZeroU16};
2
3#[cfg(feature = "serde")]
4use serde::Serialize;
5
6use crate::fmt::new_line_and_indent;
7
8#[derive(Debug, Clone, Copy, PartialEq)]
9#[cfg_attr(feature = "serde", derive(Serialize))]
10pub enum ColumnAlignment {
11 LeftJustified = 0,
12 Centered = 1,
13 RightJustified = 2,
14}
15
16#[derive(Debug, Clone, Copy, PartialEq)]
17#[cfg_attr(feature = "serde", derive(Serialize))]
18pub enum LineType {
19 Solid = 3,
20 Dashed = 4,
21}
22
23#[derive(Debug, Clone, Copy, PartialEq)]
24#[cfg_attr(feature = "serde", derive(Serialize))]
25pub enum ColumnSpec {
26 WithContent(ColumnAlignment, Option<LineType>),
27 OnlyLine(LineType),
28}
29
30#[derive(Debug, PartialEq)]
31#[cfg_attr(feature = "serde", derive(Serialize))]
32pub struct ArraySpec<'arena> {
33 pub beginning_line: Option<LineType>,
34 pub is_sub: bool,
35 pub column_spec: &'arena [ColumnSpec],
36}
37
38#[derive(Debug, Clone, Copy, PartialEq)]
39#[cfg_attr(feature = "serde", derive(Serialize))]
40pub enum Alignment {
41 Centered,
42 Cases,
43 Alternating,
44}
45
46#[derive(Debug)]
53#[cfg_attr(feature = "serde", derive(Serialize))]
54pub struct RowLabelInfo<'arena> {
55 pub tag: Option<NonZeroU16>,
56 pub link_target: Option<&'arena str>,
57}
58
59enum AlignmentType<'arena> {
60 Predefined(Alignment),
61 Custom(&'arena ArraySpec<'arena>),
62 MultLine(NonZeroU16),
63}
64
65const MTD_OPEN_STYLE: &str = "<mtd style=\"";
66const MTD_CLOSE_STYLE: &str = "\">";
67const LEFT_ALIGN: &str = "text-align: left;justify-items: start;";
68pub const RIGHT_ALIGN: &str = "text-align: right;justify-items: end;";
69const PADDING_RIGHT_ZERO: &str = "padding-right: 0;";
70const PADDING_LEFT_ZERO: &str = "padding-left: 0;";
71const PADDING_TOP_BOTTOM_ZERO: &str = "padding-top: 0;padding-bottom: 0;";
72const BORDER_RIGHT_SOLID: &str = "border-right: 0.05em solid currentcolor;";
73const BORDER_RIGHT_DASHED: &str = "border-right: 0.05em dashed currentcolor;";
74const SIMPLE_CENTERED: &str = "<mtd>";
75
76pub struct ColumnGenerator<'arena> {
77 typ: AlignmentType<'arena>,
78 column_idx: usize,
79 row_idx: usize,
80}
81
82impl<'arena> ColumnGenerator<'arena> {
83 pub fn new_predefined(align: Alignment) -> Self {
84 ColumnGenerator {
85 typ: AlignmentType::Predefined(align),
86 column_idx: 0,
87 row_idx: 0,
88 }
89 }
90
91 pub fn new_custom(array_spec: &'arena ArraySpec<'arena>) -> Self {
92 ColumnGenerator {
93 typ: AlignmentType::Custom(array_spec),
94 column_idx: 0,
95 row_idx: 0,
96 }
97 }
98
99 pub fn new_multline(num_rows: NonZeroU16) -> Self {
100 ColumnGenerator {
101 typ: AlignmentType::MultLine(num_rows),
102 column_idx: 0,
103 row_idx: 0,
104 }
105 }
106
107 pub fn reset_to_new_row(&mut self) {
108 self.column_idx = 0;
109 self.row_idx += 1;
110 }
111
112 pub fn write_next_mtd(
113 &mut self,
114 s: &mut String,
115 indent_num: usize,
116 ) -> Result<(), std::fmt::Error> {
117 new_line_and_indent(s, indent_num);
118 let column_idx = self.column_idx;
119 self.column_idx += 1;
120 match self.typ {
121 AlignmentType::Predefined(align) => {
122 let is_even = column_idx.is_multiple_of(2);
123 match align {
124 Alignment::Cases => {
125 write!(s, "{MTD_OPEN_STYLE}{LEFT_ALIGN}{PADDING_RIGHT_ZERO}")?;
126 if !is_even {
127 write!(s, "padding-left:1em;")?;
128 }
129 write!(s, "{MTD_CLOSE_STYLE}")?;
130 }
131 Alignment::Centered => {
132 write!(s, "{SIMPLE_CENTERED}")?;
133 }
134 Alignment::Alternating => {
135 write!(s, "{MTD_OPEN_STYLE}")?;
136 if is_even {
137 write!(s, "{RIGHT_ALIGN}{PADDING_RIGHT_ZERO}")?;
138 } else {
139 write!(s, "{LEFT_ALIGN}{PADDING_LEFT_ZERO}")?;
140 }
141 write!(s, "{MTD_CLOSE_STYLE}")?;
142 }
143 }
144 }
145 AlignmentType::Custom(array_spec) => {
146 let mut column_spec = array_spec
147 .column_spec
148 .get(column_idx)
149 .unwrap_or(&ColumnSpec::WithContent(ColumnAlignment::Centered, None));
150 while let ColumnSpec::OnlyLine(line_type) = column_spec {
151 column_spec = array_spec
152 .column_spec
153 .get(self.column_idx)
154 .unwrap_or(&ColumnSpec::WithContent(ColumnAlignment::Centered, None));
155 self.column_idx += 1;
156 write!(s, "{MTD_OPEN_STYLE}")?;
157 match line_type {
158 LineType::Solid => {
159 write!(s, "{BORDER_RIGHT_SOLID}")?;
160 }
161 LineType::Dashed => {
162 write!(s, "{BORDER_RIGHT_DASHED}")?;
163 }
164 }
165 if array_spec.is_sub {
166 write!(s, "{PADDING_TOP_BOTTOM_ZERO}")?;
167 }
168 write!(s, "padding-left: 0.1em;padding-right: 0.1em;")?;
169 write!(s, "\"></mtd>")?;
170 new_line_and_indent(s, indent_num);
171 }
172 match column_spec {
173 ColumnSpec::WithContent(alignment, line_type) => {
174 if matches!(alignment, ColumnAlignment::Centered)
175 && line_type.is_none()
176 && !array_spec.is_sub
177 {
178 write!(s, "{SIMPLE_CENTERED}")?;
179 return Ok(());
180 }
181 write!(s, "{MTD_OPEN_STYLE}")?;
182 match alignment {
183 ColumnAlignment::LeftJustified => {
184 write!(s, "{LEFT_ALIGN}")?;
185 }
186 ColumnAlignment::Centered => {}
187 ColumnAlignment::RightJustified => {
188 write!(s, "{RIGHT_ALIGN}")?;
189 }
190 }
191 match line_type {
192 Some(LineType::Solid) => {
193 write!(s, "{BORDER_RIGHT_SOLID}")?;
194 }
195 Some(LineType::Dashed) => {
196 write!(s, "{BORDER_RIGHT_DASHED}")?;
197 }
198 _ => {}
199 }
200 if array_spec.is_sub {
201 write!(s, "{PADDING_TOP_BOTTOM_ZERO}")?;
202 }
203 write!(s, "{MTD_CLOSE_STYLE}")?;
204 }
205 ColumnSpec::OnlyLine(_) => {}
206 }
207 }
208 AlignmentType::MultLine(num_rows) => {
209 let row_idx = self.row_idx;
210 if row_idx == 0 {
213 write!(s, "{MTD_OPEN_STYLE}{LEFT_ALIGN}{MTD_CLOSE_STYLE}")?;
214 } else if row_idx + 1 == (num_rows.get() as usize) {
215 write!(s, "{MTD_OPEN_STYLE}{RIGHT_ALIGN}{MTD_CLOSE_STYLE}")?;
216 } else {
217 write!(s, "{SIMPLE_CENTERED}")?;
218 }
219 }
220 }
221 Ok(())
222 }
223}