1use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use std::default::Default;
5use std::path::{Path, PathBuf};
6
7#[derive(Debug, Serialize, Deserialize)]
9pub struct Document {
10 pub nodes: Vec<Node>,
12 pub newline: String,
14}
15
16#[derive(Debug, Serialize, Deserialize)]
18pub enum Node {
19 Text(TextBlock),
21 Code(CodeBlock),
23 Transclusion(Transclusion),
25}
26
27impl Document {
28 pub fn new(nodes: Vec<Node>, newline: String) -> Self {
30 Document { nodes, newline }
31 }
32
33 pub fn newline(&self) -> &str {
35 &self.newline
36 }
37
38 pub fn code_blocks(&self) -> impl Iterator<Item = &CodeBlock> {
40 self.nodes.iter().filter_map(|node| match node {
41 Node::Code(block) => Some(block),
42 _ => None,
43 })
44 }
45
46 pub fn code_blocks_by_name(&self) -> HashMap<Option<&str>, Vec<&CodeBlock>> {
48 let mut code_blocks = HashMap::<_, Vec<&CodeBlock>>::new();
49
50 for block in self.code_blocks() {
51 code_blocks
52 .entry(block.name.as_deref())
53 .or_default()
54 .push(block);
55 }
56
57 code_blocks
58 }
59
60 pub fn transclusions(&self) -> impl Iterator<Item = &Transclusion> {
62 self.nodes.iter().filter_map(|node| match node {
63 Node::Transclusion(trans) => Some(trans),
64 _ => None,
65 })
66 }
67
68 pub fn entry_points(&self) -> HashMap<Option<&str>, (&Path, Option<PathBuf>)> {
70 let mut entries = HashMap::new();
71 for block in self.code_blocks() {
72 if let Some(name) = block.name.as_deref() {
73 if block.is_file {
74 entries.insert(
75 Some(name),
76 (
77 Path::new(name),
78 block.source_file.as_ref().map(|file| file.into()),
79 ),
80 );
81 }
82 }
83 }
84 entries
85 }
86}
87
88#[derive(Debug, Default, Serialize, Deserialize)]
90pub struct TextBlock {
91 pub text: Vec<String>,
93}
94
95#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
97pub struct Transclusion {
98 pub file: PathBuf,
100 pub original: String,
102}
103
104#[derive(Default, Debug, Serialize, Deserialize)]
106pub struct CodeBlock {
107 pub line_number: usize,
109 pub indent: String,
111 pub name: Option<String>,
113 pub is_unnamed: bool,
115 pub language: Option<String>,
117 pub is_hidden: bool,
119 pub is_file: bool,
121 pub is_alternative: bool,
123 pub source: Vec<Line>,
125 pub source_file: Option<String>,
127}
128
129impl CodeBlock {
130 pub fn new(
131 line_number: usize,
132 indent: String,
133 language: Option<String>,
134 alternative: bool,
135 ) -> Self {
136 CodeBlock {
137 line_number,
138 indent,
139 language,
140 is_alternative: alternative,
141 ..Default::default()
142 }
143 }
144}
145
146#[derive(Debug, Serialize, Deserialize)]
148pub enum Line {
149 Macro {
151 indent: String,
153 name: String,
155 },
156 Source {
158 indent: String,
160 source: String,
162 },
163}