use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::default::Default;
use std::path::{Path, PathBuf};
#[derive(Debug, Serialize, Deserialize)]
pub struct Document {
pub nodes: Vec<Node>,
pub newline: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum Node {
Text(TextBlock),
Code(CodeBlock),
Transclusion(Transclusion),
}
impl Document {
pub fn new(nodes: Vec<Node>, newline: String) -> Self {
Document { nodes, newline }
}
pub fn newline(&self) -> &str {
&self.newline
}
pub fn code_blocks(&self) -> impl Iterator<Item = &CodeBlock> {
self.nodes.iter().filter_map(|node| match node {
Node::Code(block) => Some(block),
_ => None,
})
}
pub fn code_blocks_by_name(&self) -> HashMap<Option<&str>, Vec<&CodeBlock>> {
let mut code_blocks = HashMap::<_, Vec<&CodeBlock>>::new();
for block in self.code_blocks() {
code_blocks
.entry(block.name.as_deref())
.or_default()
.push(block);
}
code_blocks
}
pub fn transclusions(&self) -> impl Iterator<Item = &Transclusion> {
self.nodes.iter().filter_map(|node| match node {
Node::Transclusion(trans) => Some(trans),
_ => None,
})
}
pub fn entry_points(&self) -> HashMap<Option<&str>, (&Path, Option<PathBuf>)> {
let mut entries = HashMap::new();
for block in self.code_blocks() {
if let Some(name) = block.name.as_deref() {
if block.is_file {
entries.insert(
Some(name),
(
Path::new(name),
block.source_file.as_ref().map(|file| file.into()),
),
);
}
}
}
entries
}
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct TextBlock {
pub text: Vec<String>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Transclusion {
pub file: PathBuf,
pub original: String,
}
#[derive(Default, Debug, Serialize, Deserialize)]
pub struct CodeBlock {
pub line_number: usize,
pub indent: String,
pub name: Option<String>,
pub is_unnamed: bool,
pub language: Option<String>,
pub is_hidden: bool,
pub is_file: bool,
pub is_alternative: bool,
pub source: Vec<Line>,
pub source_file: Option<String>,
}
impl CodeBlock {
pub fn new(
line_number: usize,
indent: String,
language: Option<String>,
alternative: bool,
) -> Self {
CodeBlock {
line_number,
indent,
language,
is_alternative: alternative,
..Default::default()
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub enum Line {
Macro {
indent: String,
name: String,
},
Source {
indent: String,
source: String,
},
}