1use crate::model::document::LinkType;
2use crate::model::graph::{blocks_to_markdown_sparce_skip_frontmatter, GraphInlines};
3use crate::model::{Key, NodeId};
4
5use super::config::MarkdownOptions;
6use super::graph::{blocks_to_markdown_sparce, GraphInline};
7use super::projector::Projector;
8use super::tree::Tree;
9
10#[derive(Clone, Debug, PartialEq)]
11pub enum Node {
12 Document(Key, Option<String>),
13 Section(GraphInlines),
14 Quote(),
15 BulletList(),
16 OrderedList(),
17 Leaf(GraphInlines),
18 Raw(Option<String>, String),
19 HorizontalRule(),
20 Reference(Reference),
21 Table(Table),
22}
23
24impl Node {
25 pub fn plain_text(&self) -> String {
26 match self {
27 Node::Section(inlines) => inlines.iter().map(|i| i.plain_text()).collect(),
28 Node::Leaf(inlines) => inlines.iter().map(|i| i.plain_text()).collect(),
29 Node::Reference(reference) => reference.text.clone(),
30 Node::Raw(_, content) => content.clone(),
31 _ => "".to_string(),
32 }
33 }
34
35 pub fn reference_key(&self) -> Option<Key> {
36 match self {
37 Node::Reference(reference) => Some(reference.key.clone()),
38 _ => None,
39 }
40 }
41
42 pub fn reference_text(&self) -> Option<String> {
43 match self {
44 Node::Reference(reference) => Some(reference.text.clone()),
45 _ => None,
46 }
47 }
48
49 pub fn is_reference(&self) -> bool {
50 matches!(self, Node::Reference(_))
51 }
52}
53
54#[derive(Clone, Copy, Debug, PartialEq)]
55pub enum ReferenceType {
56 Regular,
57 WikiLink,
58 WikiLinkPiped,
59}
60
61impl ReferenceType {
62 pub fn to_link_type(&self) -> LinkType {
63 match self {
64 ReferenceType::Regular => LinkType::Markdown,
65 ReferenceType::WikiLink => LinkType::WikiLink,
66 ReferenceType::WikiLinkPiped => LinkType::WikiLinkPiped,
67 }
68 }
69}
70
71#[derive(Clone, Debug, PartialEq)]
72pub struct Reference {
73 pub key: Key,
74 pub text: String,
75 pub reference_type: ReferenceType,
76}
77
78#[derive(Clone, Debug, PartialEq)]
79pub struct Table {
80 pub header: Vec<GraphInlines>,
81 pub alignment: Vec<ColumnAlignment>,
82 pub rows: Vec<Vec<GraphInlines>>,
83}
84
85#[derive(Debug, Clone, PartialEq, Copy)]
86pub enum ColumnAlignment {
87 None,
88 Left,
89 Center,
90 Right,
91}
92
93pub trait NodeIter<'a>: Sized {
94 fn next(&self) -> Option<Self>;
95 fn child(&self) -> Option<Self>;
96 fn node(&self) -> Option<Node>;
97
98 fn to_markdown(self, parent: &str, options: &MarkdownOptions) -> String {
99 let blocks = Projector::project(self, parent);
100 blocks_to_markdown_sparce(&blocks, options)
101 }
102
103 fn to_markdown_skip_frontmatter(self, parent: &str, options: &MarkdownOptions) -> String {
104 let blocks = Projector::project(self, parent);
105 blocks_to_markdown_sparce_skip_frontmatter(&blocks, options)
106 }
107
108 fn plain_text(&self) -> String {
109 self.inlines().iter().map(|i| i.plain_text()).collect()
110 }
111
112 fn to_default_markdown(self) -> String {
113 self.to_markdown("", &MarkdownOptions::default())
114 }
115
116 fn ref_type(&self) -> Option<ReferenceType> {
117 self.node().and_then(|node| {
118 if let Node::Reference(reference) = node {
119 Some(reference.reference_type)
120 } else {
121 None
122 }
123 })
124 }
125
126 fn lang(&self) -> Option<String> {
127 self.node().and_then(|node| {
128 if let Node::Raw(lang, _) = node {
129 lang.clone()
130 } else {
131 None
132 }
133 })
134 }
135
136 fn table_header(&self) -> Option<Vec<GraphInlines>> {
137 self.node().and_then(|node| {
138 if let Node::Table(table) = node {
139 Some(table.header.clone())
140 } else {
141 None
142 }
143 })
144 }
145
146 fn table_alignment(&self) -> Option<Vec<ColumnAlignment>> {
147 self.node().and_then(|node| {
148 if let Node::Table(table) = node {
149 Some(table.alignment.clone())
150 } else {
151 None
152 }
153 })
154 }
155
156 fn table_rows(&self) -> Option<Vec<Vec<GraphInlines>>> {
157 self.node().and_then(|node| {
158 if let Node::Table(table) = node {
159 Some(table.rows.clone())
160 } else {
161 None
162 }
163 })
164 }
165
166 fn content(&self) -> Option<String> {
167 self.node().and_then(|node| {
168 if let Node::Raw(_, content) = node {
169 Some(content.clone())
170 } else {
171 None
172 }
173 })
174 }
175
176 fn ref_text(&self) -> Option<String> {
177 self.node().and_then(|node| {
178 if let Node::Reference(reference) = node {
179 Some(reference.text.clone())
180 } else {
181 None
182 }
183 })
184 }
185
186 fn ref_key2(&self) -> Option<Key> {
187 self.node().and_then(|node| {
188 if let Node::Reference(reference) = node {
189 Some(reference.key.clone())
190 } else {
191 None
192 }
193 })
194 }
195
196 fn inlines(&self) -> GraphInlines {
197 self.node()
198 .map(|node| match node {
199 Node::Section(inlines) => inlines.clone(),
200 Node::Leaf(inlines) => inlines.clone(),
201 Node::Reference(reference) => vec![GraphInline::Str(reference.text)],
202 _ => vec![],
203 })
204 .unwrap_or_default()
205 }
206
207 fn is_list(&self) -> bool {
208 self.is_ordered_list() || self.is_bullet_list()
209 }
210
211 fn is_document(&self) -> bool {
212 matches!(self.node(), Some(Node::Document(_, _)))
213 }
214
215 fn is_section(&self) -> bool {
216 matches!(self.node(), Some(Node::Section(_)))
217 }
218
219 fn is_ordered_list(&self) -> bool {
220 matches!(self.node(), Some(Node::OrderedList()))
221 }
222
223 fn is_bullet_list(&self) -> bool {
224 matches!(self.node(), Some(Node::BulletList()))
225 }
226
227 fn is_reference(&self) -> bool {
228 matches!(self.node(), Some(Node::Reference(_)))
229 }
230
231 fn is_horizontal_rule(&self) -> bool {
232 matches!(self.node(), Some(Node::HorizontalRule()))
233 }
234
235 fn is_raw(&self) -> bool {
236 matches!(self.node(), Some(Node::Raw(_, _)))
237 }
238
239 fn is_leaf(&self) -> bool {
240 matches!(self.node(), Some(Node::Leaf(_)))
241 }
242
243 fn is_quote(&self) -> bool {
244 matches!(self.node(), Some(Node::Quote()))
245 }
246}
247
248pub trait NodePointer<'a>: NodeIter<'a> {
249 fn id(&self) -> Option<NodeId>;
250 fn next_id(&self) -> Option<NodeId>;
251 fn child_id(&self) -> Option<NodeId>;
252 fn prev_id(&self) -> Option<NodeId>;
253 fn to_node(&self, id: NodeId) -> Self;
254 fn to_key(&self, key: Key) -> Option<Self>;
255
256 fn at(&self, id: NodeId) -> bool {
257 self.id() == Some(id)
258 }
259
260 fn node_key(&self) -> Key {
261 self.to_document().and_then(|v| v.document_key()).unwrap()
262 }
263
264 fn is_header(&self) -> bool {
265 !self.is_in_list() && self.is_section()
266 }
267
268 fn collect_tree(self) -> Tree {
269 Tree::from_pointer(self).expect("to have node")
270 }
271
272 fn squash_tree(self, depth: u8) -> Tree {
273 Tree::squash_from_pointer(self, depth)
274 .first()
275 .cloned()
276 .unwrap()
277 }
278
279 fn to_prev(&self) -> Option<Self> {
280 self.prev_id().map(|id| self.to_node(id))
281 }
282
283 fn to_next(&self) -> Option<Self> {
284 self.next_id().map(|id| self.to_node(id))
285 }
286
287 fn to_child(&self) -> Option<Self> {
288 self.child_id().map(|id| self.to_node(id))
289 }
290
291 fn get_next_sections(&self) -> Vec<NodeId> {
292 let mut sections = vec![];
293 if self.is_section() {
294 if let Some(id) = self.id() {
295 sections.push(id);
296 }
297 }
298 if let Some(next) = self.to_next() {
299 sections.extend(next.get_next_sections());
300 }
301 sections
302 }
303
304 fn ref_key(&self) -> Option<Key> {
305 self.node().and_then(|node| {
306 if let Node::Reference(reference) = node {
307 Some(reference.key.clone())
308 } else {
309 None
310 }
311 })
312 }
313
314 fn document_key(&self) -> Option<Key> {
315 self.node().and_then(|node| {
316 if let Node::Document(key, _) = node {
317 Some(key.clone())
318 } else {
319 None
320 }
321 })
322 }
323
324 fn is_primary_section(&self) -> bool {
325 self.is_section() && self.to_prev().map(|p| p.is_document()).unwrap_or(false)
326 }
327
328 fn to_parent(&self) -> Option<Self> {
329 if let Some(prev) = self.to_prev() {
330 if let Some(id) = self.id() {
331 if prev.is_parent_of(id) {
332 return Some(prev);
333 }
334 }
335 prev.to_parent()
336 } else {
337 None
338 }
339 }
340
341 fn to_self(&self) -> Option<Self> {
342 self.id().map(|id| self.to_node(id))
343 }
344
345 fn get_list(&self) -> Option<Self> {
346 if self.is_ordered_list() || self.is_bullet_list() {
347 return self.to_self();
348 }
349 if self.is_document() {
350 return None;
351 }
352 self.to_parent().and_then(|p| p.get_list())
353 }
354
355 fn get_top_level_list(&self) -> Option<Self> {
356 if self.is_list() && !self.to_parent().map(|p| p.is_in_list()).unwrap_or(false) {
357 return self.to_self();
358 }
359 if self.is_document() {
360 return None;
361 }
362 self.to_parent().and_then(|p| p.get_top_level_list())
363 }
364
365 fn to_document(&self) -> Option<Self> {
366 if self.is_document() {
367 Some(self.to_node(self.id()?))
368 } else {
369 self.to_prev().and_then(|prev| prev.to_document())
370 }
371 }
372
373 fn is_parent_of(&self, other: NodeId) -> bool {
374 self.child_id().is_some() && self.child_id().unwrap() == other
375 }
376
377 fn get_sub_nodes(&self) -> Vec<NodeId> {
378 self.to_child()
379 .map_or(Vec::new(), |child| child.get_next_nodes())
380 }
381
382 fn get_all_sub_nodes(&self) -> Vec<NodeId> {
383 let mut nodes = vec![self.id().unwrap_or_default()];
384 if let Some(child) = self.to_child() {
385 nodes.extend(child.get_all_sub_nodes());
386 }
387 nodes.extend(
388 self.to_next()
389 .map(|n| n.get_all_sub_nodes())
390 .unwrap_or_else(Vec::new),
391 );
392 nodes
393 }
394
395 fn get_next_nodes(&self) -> Vec<NodeId> {
396 let mut nodes = vec![];
397 if let Some(id) = self.id() {
398 nodes.push(id);
399 }
400 if let Some(next) = self.to_next() {
401 nodes.extend(next.get_next_nodes());
402 }
403 nodes
404 }
405
406 fn get_sub_sections(&self) -> Vec<NodeId> {
407 if !self.is_section() {
408 panic!("get_sub_sections called on non-section node")
409 }
410 self.to_child()
411 .map(|n| n.get_next_sections())
412 .unwrap_or(vec![])
413 }
414
415 fn get_all_sub_headers(&self) -> Vec<NodeId> {
416 if !self.is_section() {
417 panic!("get_all_sub_headers called on non-section node")
418 }
419 let mut headers = vec![];
420 if let Some(id) = self.id() {
421 headers.push(id);
422 }
423 if let Some(child) = self.to_child() {
424 headers.extend(child.get_all_sub_headers());
425 }
426 headers.extend(
427 self.to_next()
428 .map(|n| n.get_all_sub_headers())
429 .unwrap_or_else(Vec::new),
430 );
431 headers
432 }
433
434 fn to_first_section_at_the_same_level(&self) -> Self {
435 self.to_prev()
436 .filter(|p| p.is_section() && p.is_prev_of(self.id().expect("Expected node ID")))
437 .map(|p| p.to_first_section_at_the_same_level())
438 .unwrap_or_else(|| self.id().map(|id| self.to_node(id)).unwrap())
439 }
440
441 fn is_prev_of(&self, other: NodeId) -> bool {
442 self.next_id().is_some() && self.next_id().unwrap() == other
443 }
444
445 fn is_in_list(&self) -> bool {
446 if self.is_ordered_list() || self.is_bullet_list() {
447 return true;
448 }
449 if self.is_document() {
450 return false;
451 }
452 self.to_parent().map(|p| p.is_in_list()).unwrap_or(false)
453 }
454
455 fn get_section(&self) -> Option<Self> {
456 if self.is_section() && !self.is_in_list() {
457 return self.id().map(|id| self.to_node(id));
458 }
459 if self.is_document() {
460 return None;
461 }
462 self.to_parent().and_then(|p| p.get_section())
463 }
464}