kladd 0.2.0

A weirdly personal markup language
Documentation
use crate::ast::{
    AstNode, AttributeKind, AttributeValue, Attributes, CodeNode, Document, HeadingNode, LinkNode,
    NamedNode, NodeKind, NodeTag, Quote, QuotedNode,
};

pub struct Visualizer {
    buf: String,
    depth: usize,
}

impl Visualizer {
    fn new() -> Self {
        Visualizer {
            buf: String::new(),
            depth: 2,
        }
    }

    fn depth_from_tag(&mut self, tag: NodeTag) {
        match tag {
            NodeTag::Start => self.depth += 2,
            NodeTag::End => self.depth -= 2,
        }
    }

    fn indent(&mut self) {
        self.push_str(&" ".repeat(self.depth));
    }

    pub(crate) fn push_str(&mut self, str: &str) {
        self.buf.push_str(str);
    }

    fn push(&mut self, char: char) {
        self.buf.push(char);
    }
}

pub fn visualize_document(doc: &Document) -> String {
    let mut vis = Visualizer::new();

    vis.push_str("doc");
    vis.push('\n');
    for node in &doc.body {
        visualize_node_inner(node, &mut vis);
    }

    vis.buf
}

pub fn visualize_nodes(nodes: &[AstNode]) -> String {
    let mut vis = Visualizer::new();
    vis.depth = 0;

    for node in nodes {
        visualize_node_inner(node, &mut vis);
    }

    vis.buf
}

fn write_attribute((kind, value): (&AttributeKind, &AttributeValue), vis: &mut Visualizer) {
    vis.push(' ');

    match kind {
        AttributeKind::Class => vis.push_str("class"),
        AttributeKind::Id => vis.push_str("id"),
        AttributeKind::Href => vis.push_str("href"),
        AttributeKind::Attr(v) => vis.push_str(v),
    }
    vis.push('=');
    match value {
        AttributeValue::String(v) => vis.push_str(v),
        AttributeValue::Boolean => vis.push_str("true"),
    }
}

fn write_attributes(attrs: &Attributes, vis: &mut Visualizer) {
    let len = attrs.len();
    for (i, attr) in attrs.iter().enumerate() {
        write_attribute(attr, vis);
        if i < len {
            vis.push(',');
        }
    }
}

fn visualize_node_inner(node: &AstNode, buf: &mut Visualizer) {
    use NodeKind::*;
    use NodeTag::*;

    match (&node.kind, node.tag) {
        (Heading(HeadingNode { level }), Start) => {
            buf.indent();
            buf.push_str("heading level=");
            buf.push_str(&level.to_string());
            if let Some(attrs) = node.attributes.inner() {
                write_attributes(attrs, buf);
            }

            buf.push('\n');
            buf.depth_from_tag(node.tag);
        }
        (Heading(_), End) => buf.depth_from_tag(node.tag),
        (Paragraph, Start) => {
            buf.indent();
            buf.push('p');
            if let Some(attrs) = node.attributes.inner() {
                write_attributes(attrs, buf);
            }

            buf.push('\n');
            buf.depth_from_tag(node.tag);
        }
        (Paragraph, End) => buf.depth_from_tag(node.tag),
        (Block, _) => {}
        (Section, Start) => {
            buf.indent();
            buf.push_str("section");
            if let Some(attrs) = node.attributes.inner() {
                write_attributes(attrs, buf);
            }

            buf.push('\n');
            buf.depth_from_tag(node.tag);
        }
        (Section, End) => buf.depth_from_tag(node.tag),
        (NamedBlock(NamedNode { name }), Start) => {
            buf.indent();
            buf.push_str(name);
            if let Some(attrs) = node.attributes.inner() {
                write_attributes(attrs, buf);
            }

            buf.push('\n');
            buf.depth_from_tag(node.tag);
        }
        (NamedBlock(_), End) => buf.depth_from_tag(node.tag),
        (CodeBlock(CodeNode { language, .. }), _) => {
            buf.indent();
            buf.push_str("code");

            if let Some(attrs) = node.attributes.inner() {
                write_attributes(attrs, buf);
            }

            buf.push('\n');
            buf.depth_from_tag(NodeTag::Start);
            buf.indent();
            match language {
                Some(lang) => buf.push_str(lang),
                None => buf.push_str("unknown language"),
            };
            buf.depth_from_tag(NodeTag::End);
            buf.push('\n');
        }
        (Text(str), _) => {
            buf.indent();
            buf.push_str("str text = \"");
            buf.push_str(str);
            buf.push_str("\"\n");
        }
        (Strong, Start) => {
            buf.indent();
            buf.push_str("strong");
            if let Some(attrs) = node.attributes.inner() {
                write_attributes(attrs, buf);
            }
            buf.push('\n');
            buf.depth_from_tag(node.tag);
        }
        (Strong, End) => buf.depth_from_tag(node.tag),
        (Italic, Start) => {
            buf.indent();
            buf.push_str("emph");
            if let Some(attrs) = node.attributes.inner() {
                write_attributes(attrs, buf);
            }
            buf.push('\n');
            buf.depth_from_tag(node.tag);
        }
        (Italic, End) => buf.depth_from_tag(node.tag),
        (Underline, Start) => {
            buf.indent();
            buf.push_str("under");
            if let Some(attrs) = node.attributes.inner() {
                write_attributes(attrs, buf);
            }
            buf.push('\n');
            buf.depth_from_tag(node.tag);
        }
        (Underline, End) => buf.depth_from_tag(node.tag),
        (Highlight, Start) => {
            buf.indent();
            buf.push_str("mark");
            if let Some(attrs) = node.attributes.inner() {
                write_attributes(attrs, buf);
            }
            buf.push('\n');
            buf.depth_from_tag(node.tag);
        }
        (Highlight, End) => buf.depth_from_tag(node.tag),
        (Strikethrough, Start) => {
            buf.indent();
            buf.push_str("strike");
            if let Some(attrs) = node.attributes.inner() {
                write_attributes(attrs, buf);
            }
            buf.push('\n');
            buf.depth_from_tag(node.tag);
        }
        (Strikethrough, End) => buf.depth_from_tag(node.tag),
        (Superscript, Start) => {
            buf.indent();
            buf.push_str("sup");
            if let Some(attrs) = node.attributes.inner() {
                write_attributes(attrs, buf);
            }
            buf.push('\n');
            buf.depth_from_tag(node.tag);
        }
        (Superscript, End) => buf.depth_from_tag(node.tag),
        (Subscript, Start) => {
            buf.indent();
            buf.push_str("sub");
            if let Some(attrs) = node.attributes.inner() {
                write_attributes(attrs, buf);
            }
            buf.push('\n');
            buf.depth_from_tag(node.tag);
        }
        (Subscript, End) => buf.depth_from_tag(node.tag),
        (Naked, Start) => {
            buf.indent();
            buf.push_str("@");
            if let Some(attrs) = node.attributes.inner() {
                write_attributes(attrs, buf);
            }
            buf.push('\n');
            buf.depth_from_tag(node.tag);
        }
        (Naked, End) => buf.depth_from_tag(node.tag),
        (Quoted(QuotedNode { quote }), Start) => {
            buf.indent();
            buf.push_str(match quote {
                Quote::Single => "single_quoted",
                Quote::Double => "double_quoted",
            });

            if let Some(attrs) = node.attributes.inner() {
                write_attributes(attrs, buf);
            }
            buf.push('\n');
            buf.depth_from_tag(node.tag);
        }
        (Quoted(_), End) => buf.depth_from_tag(node.tag),
        (Code(CodeNode { language, .. }), _) => {
            buf.indent();
            buf.push('@');
            buf.push_str("code");
            if let Some(attrs) = node.attributes.inner() {
                write_attributes(attrs, buf);
            }
            buf.push('\n');
            buf.indent();
            match language {
                Some(lang) => buf.push_str(lang),
                None => buf.push_str("unknown language"),
            }
        }
        (Link(LinkNode { href }), Start) => {
            buf.indent();
            buf.push_str("link");

            let attr = (
                &AttributeKind::Href,
                &AttributeValue::String(href.to_owned()),
            );
            write_attribute(attr, buf);
            if let Some(attrs) = node.attributes.inner() {
                write_attributes(attrs, buf);
            }
            buf.push('\n');
            buf.depth_from_tag(node.tag);
        }
        (Link(_), End) => buf.depth_from_tag(node.tag),
        (Custom(NamedNode { name }), Start) => {
            buf.indent();
            buf.push_str("@");
            buf.push_str(name);
            if let Some(attrs) = node.attributes.inner() {
                write_attributes(attrs, buf);
            }
            buf.push('\n');
            buf.depth_from_tag(node.tag);
        }
        (Custom(_), End) => buf.depth_from_tag(node.tag),
        (Softbreak, Start) => {}
        (Softbreak, End) => buf.depth_from_tag(node.tag),
    }
}