use std::collections::HashMap;
use std::iter::FromIterator;
use super::code::CodeBlock;
use super::text::TextBlock;
use super::{CompileError, CompileErrorKind};
use crate::parser::Printer;
#[derive(Debug)]
pub(crate) enum Node<'a> {
Text(TextBlock<'a>),
Code(CodeBlock<'a>),
}
#[derive(Debug)]
pub struct Ast<'a> {
nodes: Vec<Node<'a>>,
}
impl<'a> Ast<'a> {
pub(crate) fn new(nodes: Vec<Node<'a>>) -> Self {
Ast { nodes }
}
pub(crate) fn code_blocks(&self, language: Option<&str>) -> HashMap<Option<&str>, CodeBlock> {
let mut code_blocks = HashMap::new();
for node in &self.nodes {
if let Node::Code(block) = node {
if let Some(language) = language {
if let Some(block_language) = &block.language {
if language != block_language {
continue;
}
}
}
code_blocks
.entry(block.name.as_ref().map(|x| &x[..])) .and_modify(|existing: &mut CodeBlock<'a>| existing.append(block))
.or_insert_with(|| block.clone());
}
}
code_blocks
}
pub(crate) fn print_docs<P: Printer>(&self, printer: &P) -> String {
let mut output = String::new();
for node in &self.nodes {
match node {
Node::Text(text_block) => output.push_str(&printer.print_text_block(text_block)),
Node::Code(code_block) => output.push_str(
&printer
.print_code_block(code_block)
.split("\n")
.map(|line| {
if line.is_empty() {
line.to_string()
} else {
format!("{}{}", code_block.indent, line)
}
})
.collect::<Vec<_>>()
.join("\n"),
),
}
}
output
}
pub(crate) fn print_code(
&self,
entrypoint: Option<&str>,
language: Option<&str>,
) -> Result<String, CompileError> {
let code_blocks = self.code_blocks(language);
code_blocks
.get(&entrypoint)
.map(|entrypoint| entrypoint.compile(&code_blocks))
.unwrap_or(Err(CompileError::Single {
line_number: 0,
kind: CompileErrorKind::MissingEntrypoint,
}))
}
}
impl<'a> FromIterator<Node<'a>> for Ast<'a> {
fn from_iter<I: IntoIterator<Item = Node<'a>>>(iter: I) -> Self {
Self::new(iter.into_iter().collect())
}
}
impl<'a> From<Vec<Node<'a>>> for Ast<'a> {
fn from(nodes: Vec<Node<'a>>) -> Self {
Self::new(nodes)
}
}