1use std::mem::discriminant;
2
3#[derive(Debug, PartialEq)]
4pub struct Document {
5 pub(crate) blocks: Vec<Block>,
6}
7
8impl Document {
9 pub fn blocks(self) -> Vec<Block> {
10 self.blocks
11 }
12}
13
14#[derive(Debug, PartialEq)]
15pub enum Block {
16 Line(Line),
17 EmptyLine(EmptyLine),
18 Section(Section),
19 Ruler(Ruler),
20}
21
22#[derive(Debug, PartialEq)]
23pub struct Line {
24 pub nodes: Vec<InlineNode>,
25 pub alignment: Alignment,
26}
27
28impl Default for Line {
29 fn default() -> Self {
30 Self::new()
31 }
32}
33
34impl Line {
35 fn new() -> Self {
36 Self {
37 nodes: Vec::new(),
38 alignment: Alignment::default(),
39 }
40 }
41
42 pub(crate) fn add_node(&mut self, node: InlineNode) {
43 self.nodes.push(node);
44 }
45}
46
47#[derive(Debug, PartialEq)]
48pub struct Section {
49 pub level: u8,
50 pub header: Option<Line>,
51 pub children: Vec<ChildBlock>,
52 pub(crate) current_child: Option<ChildBlock>,
53 pub(crate) current_alignment: Alignment,
54}
55
56#[derive(Debug, PartialEq)]
57pub enum ChildBlock {
58 Line(Line),
59 EmptyLine(EmptyLine),
60 Ruler(Ruler),
61}
62
63impl Default for Section {
64 fn default() -> Self {
65 Self::new()
66 }
67}
68
69impl Section {
70 fn new() -> Self {
71 Self {
72 level: 1,
73 header: None,
74 children: Vec::new(),
75 current_child: None,
76 current_alignment: Alignment::default(),
77 }
78 }
79
80 pub(crate) fn add_to_header(&mut self, node: InlineNode) {
81 if let Some(header) = self.header.as_mut() {
82 header.add_node(node);
83 } else {
84 self.header = Some(Line::new());
85 self.add_to_header(node);
86 }
87 }
88
89 pub(crate) fn set_header_alignment(&mut self, alignment: Alignment) {
90 if let Some(header) = self.header.as_mut() {
91 header.alignment = alignment;
92 }
93 }
94
95 pub(crate) fn finish_child(&mut self) {
96 if let Some(child) = self.current_child.take() {
97 self.children.push(child);
98 }
99 }
100
101 fn start_paragraph(&mut self) {
102 self.finish_child();
103 self.current_child = Some(ChildBlock::Line(Line {
104 alignment: self.current_alignment,
105 ..Default::default()
106 }));
107 }
108
109 pub(crate) fn set_alignment(&mut self, alignment: Alignment) {
110 self.current_alignment = alignment;
111 if let Some(child) = self.current_child.as_mut() {
112 match child {
113 ChildBlock::Line(paragraph) => paragraph.alignment = alignment,
114 ChildBlock::EmptyLine(empty_line) => empty_line.alignment = alignment,
115 ChildBlock::Ruler(_) => (),
116 }
117 }
118 }
119
120 pub(crate) fn add_inline_node(&mut self, node: InlineNode) {
121 if let Some(child) = self.current_child.as_mut() {
122 match child {
123 ChildBlock::Line(paragraph) => paragraph.add_node(node),
124 ChildBlock::EmptyLine(_) | ChildBlock::Ruler(_) => {
125 self.start_paragraph();
126 self.add_inline_node(node);
127 }
128 }
129 } else {
130 self.start_paragraph();
131 self.add_inline_node(node);
132 }
133 }
134
135 pub(crate) fn add_ruler(&mut self, line: Ruler) {
136 self.finish_child();
137 self.current_child = Some(ChildBlock::Ruler(line));
138 }
139
140 pub(crate) fn add_empty_line(&mut self, empty_line: EmptyLine) {
141 self.finish_child();
142 self.current_child = Some(ChildBlock::EmptyLine(empty_line));
143 }
144}
145
146#[derive(Debug, PartialEq, Default)]
147pub struct EmptyLine {
148 pub style: StyleSet,
149 pub alignment: Alignment,
150}
151
152#[derive(Debug, PartialEq)]
153pub struct Ruler {
154 pub symbol: Option<String>,
155 pub style: StyleSet,
156}
157
158impl Default for Ruler {
159 fn default() -> Self {
160 Self::new()
161 }
162}
163
164impl Ruler {
165 fn new() -> Self {
166 Self {
167 symbol: None,
168 style: StyleSet::default(),
169 }
170 }
171}
172
173#[derive(Debug, PartialEq)]
174pub enum InlineNode {
175 Text { style: StyleSet, text: String },
176 Newline { style: StyleSet },
177 Link { style: StyleSet, link: Link },
178 Field { style: StyleSet, field: Field },
179}
180
181#[derive(Debug, Default, Clone, Copy, PartialEq)]
182pub enum Alignment {
183 #[default]
184 Default,
185 Left,
186 Center,
187 Right,
188}
189
190#[derive(Debug, Clone, Default, PartialEq, Eq)]
191pub struct StyleSet(pub Vec<Style>);
192
193#[derive(Debug, Clone, PartialEq, Eq)]
194pub enum Style {
195 Bold,
196 Italic,
197 Underline,
198 ForegroundColor(String),
199 BackgroundColor(String),
200}
201
202impl StyleSet {
203 fn pos_of(&self, modifier: &Style) -> Option<usize> {
204 self.0
205 .iter()
206 .position(|m| discriminant(m) == discriminant(modifier))
207 }
208
209 pub(crate) fn set(&mut self, modifier: Style) {
210 self.unset(&modifier);
211 self.0.push(modifier);
212 }
213
214 pub(crate) fn unset(&mut self, modifier: &Style) {
215 while let Some(i) = self.pos_of(modifier) {
216 self.0.remove(i);
217 }
218 }
219}
220
221#[derive(Debug, Clone, PartialEq)]
222pub struct Link {
223 pub label: Option<String>,
224 pub url: String,
225 pub fields: Vec<String>,
226}
227
228#[derive(Debug, Clone, PartialEq)]
229pub struct Field {
230 pub kind: FieldKind,
231 pub name: String,
232 pub value: String,
233}
234
235#[derive(Debug, Clone, PartialEq)]
236pub enum FieldKind {
237 Checkbox { pre_checked: bool },
238 Radio { pre_checked: bool },
239 Input { hidden: bool, size: Option<u8> },
240}