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
46enum AlignmentType<'arena> {
47 Predefined(Alignment),
48 Custom(&'arena ArraySpec<'arena>),
49 MultLine(NonZeroU16),
50}
51
52const MTD_OPEN_STYLE: &str = "<mtd style=\"";
53const MTD_CLOSE_STYLE: &str = "\">";
54const LEFT_ALIGN: &str = "text-align: left;justify-items: start;";
55pub const RIGHT_ALIGN: &str = "text-align: right;justify-items: end;";
56const PADDING_RIGHT_ZERO: &str = "padding-right: 0;";
57const PADDING_LEFT_ZERO: &str = "padding-left: 0;";
58const PADDING_TOP_BOTTOM_ZERO: &str = "padding-top: 0;padding-bottom: 0;";
59const BORDER_RIGHT_SOLID: &str = "border-right: 0.05em solid currentcolor;";
60const BORDER_RIGHT_DASHED: &str = "border-right: 0.05em dashed currentcolor;";
61const SIMPLE_CENTERED: &str = "<mtd>";
62
63pub struct ColumnGenerator<'arena> {
64 typ: AlignmentType<'arena>,
65 column_idx: usize,
66 row_idx: usize,
67}
68
69impl<'arena> ColumnGenerator<'arena> {
70 pub fn new_predefined(align: Alignment) -> Self {
71 ColumnGenerator {
72 typ: AlignmentType::Predefined(align),
73 column_idx: 0,
74 row_idx: 0,
75 }
76 }
77
78 pub fn new_custom(array_spec: &'arena ArraySpec<'arena>) -> Self {
79 ColumnGenerator {
80 typ: AlignmentType::Custom(array_spec),
81 column_idx: 0,
82 row_idx: 0,
83 }
84 }
85
86 pub fn new_multline(num_rows: NonZeroU16) -> Self {
87 ColumnGenerator {
88 typ: AlignmentType::MultLine(num_rows),
89 column_idx: 0,
90 row_idx: 0,
91 }
92 }
93
94 pub fn reset_to_new_row(&mut self) {
95 self.column_idx = 0;
96 self.row_idx += 1;
97 }
98
99 pub fn write_next_mtd(
100 &mut self,
101 s: &mut String,
102 indent_num: usize,
103 ) -> Result<(), std::fmt::Error> {
104 new_line_and_indent(s, indent_num);
105 let column_idx = self.column_idx;
106 self.column_idx += 1;
107 match self.typ {
108 AlignmentType::Predefined(align) => {
109 let is_even = column_idx.is_multiple_of(2);
110 match align {
111 Alignment::Cases => {
112 write!(s, "{MTD_OPEN_STYLE}{LEFT_ALIGN}{PADDING_RIGHT_ZERO}")?;
113 if !is_even {
114 write!(s, "padding-left:1em;")?;
115 }
116 write!(s, "{MTD_CLOSE_STYLE}")?;
117 }
118 Alignment::Centered => {
119 write!(s, "{SIMPLE_CENTERED}")?;
120 }
121 Alignment::Alternating => {
122 write!(s, "{MTD_OPEN_STYLE}")?;
123 if is_even {
124 write!(s, "{RIGHT_ALIGN}{PADDING_RIGHT_ZERO}")?;
125 } else {
126 write!(s, "{LEFT_ALIGN}{PADDING_LEFT_ZERO}")?;
127 }
128 write!(s, "{MTD_CLOSE_STYLE}")?;
129 }
130 }
131 }
132 AlignmentType::Custom(array_spec) => {
133 let mut column_spec = array_spec
134 .column_spec
135 .get(column_idx)
136 .unwrap_or(&ColumnSpec::WithContent(ColumnAlignment::Centered, None));
137 while let ColumnSpec::OnlyLine(line_type) = column_spec {
138 column_spec = array_spec
139 .column_spec
140 .get(self.column_idx)
141 .unwrap_or(&ColumnSpec::WithContent(ColumnAlignment::Centered, None));
142 self.column_idx += 1;
143 write!(s, "{MTD_OPEN_STYLE}")?;
144 match line_type {
145 LineType::Solid => {
146 write!(s, "{BORDER_RIGHT_SOLID}")?;
147 }
148 LineType::Dashed => {
149 write!(s, "{BORDER_RIGHT_DASHED}")?;
150 }
151 }
152 if array_spec.is_sub {
153 write!(s, "{PADDING_TOP_BOTTOM_ZERO}")?;
154 }
155 write!(s, "padding-left: 0.1em;padding-right: 0.1em;")?;
156 write!(s, "\"></mtd>")?;
157 new_line_and_indent(s, indent_num);
158 }
159 match column_spec {
160 ColumnSpec::WithContent(alignment, line_type) => {
161 if matches!(alignment, ColumnAlignment::Centered)
162 && line_type.is_none()
163 && !array_spec.is_sub
164 {
165 write!(s, "{SIMPLE_CENTERED}")?;
166 return Ok(());
167 }
168 write!(s, "{MTD_OPEN_STYLE}")?;
169 match alignment {
170 ColumnAlignment::LeftJustified => {
171 write!(s, "{LEFT_ALIGN}")?;
172 }
173 ColumnAlignment::Centered => {}
174 ColumnAlignment::RightJustified => {
175 write!(s, "{RIGHT_ALIGN}")?;
176 }
177 }
178 match line_type {
179 Some(LineType::Solid) => {
180 write!(s, "{BORDER_RIGHT_SOLID}")?;
181 }
182 Some(LineType::Dashed) => {
183 write!(s, "{BORDER_RIGHT_DASHED}")?;
184 }
185 _ => {}
186 }
187 if array_spec.is_sub {
188 write!(s, "{PADDING_TOP_BOTTOM_ZERO}")?;
189 }
190 write!(s, "{MTD_CLOSE_STYLE}")?;
191 }
192 ColumnSpec::OnlyLine(_) => {}
193 }
194 }
195 AlignmentType::MultLine(num_rows) => {
196 let row_idx = self.row_idx;
197 if row_idx == 0 {
200 write!(s, "{MTD_OPEN_STYLE}{LEFT_ALIGN}{MTD_CLOSE_STYLE}")?;
201 } else if row_idx + 1 == (num_rows.get() as usize) {
202 write!(s, "{MTD_OPEN_STYLE}{RIGHT_ALIGN}{MTD_CLOSE_STYLE}")?;
203 } else {
204 write!(s, "{SIMPLE_CENTERED}")?;
205 }
206 }
207 };
208 Ok(())
209 }
210}