use crate::model::graph::{GraphBlock, GraphInline, GraphInlines};
use crate::model::node::{Node, NodeIter, ReferenceType};
pub struct Projector {
header_level: usize,
parent: String,
}
impl Projector {
pub fn project<'a>(iter: impl NodeIter<'a>, parent: &str) -> Vec<GraphBlock> {
Projector {
header_level: 0,
parent: parent.to_string(),
}
.project_node(iter)
}
fn with(&self, header_level: usize) -> Projector {
Projector {
header_level,
parent: self.parent.clone(),
}
}
fn resolve_inlines(&self, inlines: GraphInlines) -> GraphInlines {
inlines
.into_iter()
.map(|i| self.resolve_inline(i))
.collect()
}
fn resolve_inline(&self, inline: GraphInline) -> GraphInline {
match inline {
GraphInline::Reference(reference) => {
let url = reference.key.to_rel_link_url(&self.parent);
let inlines = match reference.reference_type {
ReferenceType::WikiLink => vec![],
_ => vec![GraphInline::Str(reference.text)],
};
GraphInline::Link(
url,
String::default(),
reference.reference_type.to_link_type(),
inlines,
)
}
GraphInline::Emph(v) => GraphInline::Emph(self.resolve_inlines(v)),
GraphInline::Strong(v) => GraphInline::Strong(self.resolve_inlines(v)),
GraphInline::Strikeout(v) => GraphInline::Strikeout(self.resolve_inlines(v)),
GraphInline::Underline(v) => GraphInline::Underline(self.resolve_inlines(v)),
GraphInline::Superscript(v) => GraphInline::Superscript(self.resolve_inlines(v)),
GraphInline::Subscript(v) => GraphInline::Subscript(self.resolve_inlines(v)),
GraphInline::SmallCaps(v) => GraphInline::SmallCaps(self.resolve_inlines(v)),
GraphInline::Image(url, title, v) => {
GraphInline::Image(url, title, self.resolve_inlines(v))
}
GraphInline::Link(url, title, lt, v) => {
GraphInline::Link(url, title, lt, self.resolve_inlines(v))
}
other => other,
}
}
fn project_node<'a>(&self, iter: impl NodeIter<'a>) -> Vec<GraphBlock> {
let mut blocks = vec![];
if iter.node().is_none() {
return blocks;
}
match iter.node().unwrap() {
Node::Document(_, frontmatter) => {
if let Some(mapping) = frontmatter {
blocks.push(GraphBlock::Frontmatter(mapping.clone()));
}
if let Some(child) = iter.child() {
blocks.extend(self.with(self.header_level).project_node(child));
}
}
Node::Section(_) => {
blocks.push(GraphBlock::Header(
self.header_level as u8 + 1,
self.resolve_inlines(iter.inlines()),
));
if let Some(child) = iter.child() {
blocks.extend(self.with(self.header_level + 1).project_node(child));
}
}
Node::Quote() => {
if let Some(child) = iter.child() {
blocks.push(GraphBlock::BlockQuote(self.with(0).project_node(child)));
}
}
Node::BulletList() => {
if let Some(child) = iter.child() {
blocks.push(GraphBlock::BulletList(
self.with(0).project_list_item(child),
));
}
}
Node::OrderedList() => {
if let Some(child) = iter.child() {
blocks.push(GraphBlock::OrderedList(
self.with(0).project_list_item(child),
));
}
}
Node::Leaf(_) => {
blocks.push(GraphBlock::Para(self.resolve_inlines(iter.inlines())));
}
Node::Raw(_, _) => {
blocks.push(GraphBlock::CodeBlock(
iter.lang(),
iter.content().unwrap_or_default(),
));
}
Node::HorizontalRule() => {
blocks.push(GraphBlock::HorizontalRule);
}
Node::Reference(_) => {
let inlines = match iter.ref_type().unwrap() {
ReferenceType::Regular => self.resolve_inlines(iter.inlines()),
ReferenceType::WikiLink => vec![],
ReferenceType::WikiLinkPiped => {
vec![GraphInline::Str(iter.ref_text().unwrap_or_default())]
}
};
let link = GraphInline::Link(
iter.ref_key2().unwrap().to_rel_link_url(&self.parent),
String::default(),
iter.ref_type().unwrap().to_link_type(),
inlines,
);
blocks.push(GraphBlock::Para(vec![link]));
}
Node::Table(_) => {
blocks.push(GraphBlock::Table(
iter.table_header()
.unwrap_or_default()
.into_iter()
.map(|cell| self.resolve_inlines(cell))
.collect(),
iter.table_alignment().unwrap_or_default(),
iter.table_rows()
.unwrap_or_default()
.into_iter()
.map(|row| {
row.into_iter()
.map(|cell| self.resolve_inlines(cell))
.collect()
})
.collect(),
));
}
}
if let Some(next) = iter.next() {
blocks.extend(self.with(self.header_level).project_node(next));
}
blocks
}
fn project_list_item<'a>(&self, iter: impl NodeIter<'a>) -> Vec<Vec<GraphBlock>> {
let mut items: Vec<Vec<GraphBlock>> = vec![];
if iter.node().is_none() {
return items;
}
if iter.child().map(|n| n.is_leaf()).unwrap_or(false) {
items.push(vec![GraphBlock::Para(self.resolve_inlines(iter.inlines()))]);
} else {
items.push(vec![GraphBlock::Plain(self.resolve_inlines(iter.inlines()))]);
}
if let Some(sub_list) = iter.child().map(|child| self.with(0).project_node(child)) {
sub_list
.iter()
.for_each(|item| items.last_mut().unwrap().push(item.clone()))
}
if let Some(blocks) = iter
.next()
.map(|next| self.with(self.header_level).project_list_item(next))
{
items.append(blocks.clone().as_mut())
}
items
}
}