micron_parser/parsing/
model.rs

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}