about summary refs log tree commit diff
path: root/markup/src/lineup/tree.rs
diff options
context:
space:
mode:
authorgennyble <gen@nyble.dev>2026-09-21 14:23:07 -0500
committergennyble <gen@nyble.dev>2026-09-21 14:23:07 -0500
commitbe872aa0cd61ea16b95eddcd68c6a72f0fe3af96 (patch)
tree8935da5f9639b939a0e5ecd2bba3072d71d602bc /markup/src/lineup/tree.rs
parent178ca3c066a1ef920b112da741625099b6dc07da (diff)
massive reorg
Diffstat (limited to 'markup/src/lineup/tree.rs')
-rw-r--r--markup/src/lineup/tree.rs147
1 files changed, 147 insertions, 0 deletions
diff --git a/markup/src/lineup/tree.rs b/markup/src/lineup/tree.rs
new file mode 100644
index 0000000..dcb1cb4
--- /dev/null
+++ b/markup/src/lineup/tree.rs
@@ -0,0 +1,147 @@
+use std::{borrow::Cow, rc::Rc};
+
+use crate::InterlinkSolver;
+
+pub struct Document<'raw> {
+	pub(crate) nodes: Vec<TreeNode<'raw>>,
+	pub(crate) solver: Rc<dyn InterlinkSolver>,
+}
+
+impl<'raw> Document<'raw> {
+	pub fn set_interlink_solver(&mut self, solver: Rc<dyn InterlinkSolver>) {
+		self.solver = solver;
+	}
+
+	pub fn html(self) -> String {
+		TreeNode::html_nodes(&self.nodes, Rc::clone(&self.solver))
+	}
+}
+
+#[derive(Clone, Debug, PartialEq)]
+pub enum TreeNode<'raw> {
+	Text(&'raw str),
+	Paragraph {
+		children: Vec<TreeNode<'raw>>,
+	},
+	Interlink {
+		children: Vec<TreeNode<'raw>>,
+	},
+	Link {
+		kind: LinkKind,
+		name: Option<Vec<TreeNode<'raw>>>,
+		children: Vec<TreeNode<'raw>>,
+	},
+	StyledText {
+		style: NodeStyle,
+		children: Vec<TreeNode<'raw>>,
+	},
+}
+
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum LinkKind {
+	External,
+}
+
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum NodeStyle {
+	Bold,
+	Code,
+	Italic,
+}
+
+impl<'raw> TreeNode<'raw> {
+	pub fn html(&self, solver: Rc<dyn InterlinkSolver>) -> Cow<'raw, str> {
+		match self {
+			TreeNode::Text(str) => Cow::Borrowed(str),
+			TreeNode::Paragraph { children } => {
+				let inner = Self::html_nodes(children.as_slice(), solver);
+				Cow::Owned(format!("<p>{inner}</p>"))
+			}
+			TreeNode::Interlink { children } => {
+				let txt = Self::text_nodes(&children, Rc::clone(&solver));
+
+				let title = solver.link_title(&txt);
+				let location = solver.link_location(&txt);
+
+				Cow::Owned(format!("<a href=\"{location}\">{title}</a>"))
+			}
+			TreeNode::Link {
+				kind,
+				name,
+				children,
+			} => {
+				let txt = Self::text_nodes(&children, Rc::clone(&solver));
+
+				let title = match name.as_deref() {
+					None => Cow::Borrowed(txt.as_str()),
+					Some(name) => {
+						let txt = Self::text_nodes(name, Rc::clone(&solver));
+						Cow::Owned(txt)
+					}
+				};
+				let location = txt.as_str();
+
+				Cow::Owned(format!("<a href=\"{location}\">{title}</a>"))
+			}
+			TreeNode::StyledText { style, children } => {
+				let (open, close) = match style {
+					NodeStyle::Bold => ("<b>", "</b>"),
+					NodeStyle::Code => ("<code>", "</code>"),
+					NodeStyle::Italic => ("<i>", "</i>"),
+				};
+
+				let inner = Self::html_nodes(children.as_slice(), solver);
+				Cow::Owned(format!("{open}{inner}{close}"))
+			}
+		}
+	}
+
+	pub fn text(&self, solver: Rc<dyn InterlinkSolver>) -> Cow<'raw, str> {
+		match self {
+			TreeNode::Text(str) => Cow::Borrowed(str),
+			TreeNode::Paragraph { children } => Cow::Owned(Self::text_nodes(children, solver)),
+			TreeNode::Interlink { children } => {
+				let text = Self::text_nodes(children, Rc::clone(&solver));
+				Cow::Owned(solver.link_title(&text).into_owned())
+			}
+			TreeNode::Link {
+				kind: _,
+				name,
+				children,
+			} => match name.as_deref() {
+				None => {
+					let text = Self::text_nodes(children, Rc::clone(&solver));
+					Cow::Owned(solver.link_title(&text).into_owned())
+				}
+				Some(name) => {
+					let text = Self::text_nodes(name, Rc::clone(&solver));
+					Cow::Owned(solver.link_title(&text).into_owned())
+				}
+			},
+			TreeNode::StyledText { style: _, children } => {
+				Cow::Owned(Self::text_nodes(children, solver))
+			}
+		}
+	}
+
+	fn text_nodes(nodes: &[TreeNode<'raw>], solver: Rc<dyn InterlinkSolver>) -> String {
+		let mut ret = String::new();
+		for node in nodes {
+			let node_txt = &node.text(Rc::clone(&solver));
+			if !node_txt.is_empty() {
+				ret.push_str(node_txt);
+			}
+		}
+
+		ret
+	}
+
+	fn html_nodes(nodes: &[TreeNode<'raw>], solver: Rc<dyn InterlinkSolver>) -> String {
+		let mut ret = String::new();
+		for node in nodes {
+			ret.push_str(&node.html(Rc::clone(&solver)));
+		}
+
+		ret
+	}
+}