1use std::path::PathBuf;
11
12use crate::html::{render_fragment, render_html5, render_pseudoxml};
13use crate::nodes::{Block, Document};
14use crate::parser::Parser;
15use crate::settings::{Halt, Settings, WriterName};
16use crate::transforms::apply_transforms;
17
18pub fn publish_doctree_with(
23 source: &str,
24 settings: &Settings,
25 base_dir: Option<PathBuf>,
26) -> Result<Document, Halt> {
27 let mut doc = publish_pipeline(source, settings, base_dir)?;
28 strip_messages_section(&mut doc);
29 Ok(doc)
30}
31
32pub fn publish_string_with(
37 source: &str,
38 settings: &Settings,
39 base_dir: Option<PathBuf>,
40) -> Result<(String, Document), Halt> {
41 let mut doc = publish_pipeline(source, settings, base_dir)?;
42 filter_messages(&mut doc, settings.report_level);
43 let out = match settings.writer_name {
44 WriterName::Html5 => render_html5(&doc),
45 WriterName::PseudoXml => render_pseudoxml(&doc),
46 WriterName::Null => String::new(),
47 };
48 Ok((out, doc))
49}
50
51pub fn publish_fragment_with(
53 source: &str,
54 settings: &Settings,
55 base_dir: Option<PathBuf>,
56) -> Result<String, Halt> {
57 let doc = publish_doctree_with(source, settings, base_dir)?;
58 Ok(render_fragment(&doc))
59}
60
61fn publish_pipeline(
62 source: &str,
63 settings: &Settings,
64 base_dir: Option<PathBuf>,
65) -> Result<Document, Halt> {
66 let mut doc = Parser::new(source, base_dir).run();
67 apply_transforms(&mut doc);
68 check_halt(&doc, settings)?;
69 Ok(doc)
70}
71
72fn strip_messages_section(doc: &mut Document) {
75 if let Some(Block::Section(s)) = doc.children.last()
76 && s.classes.iter().any(|c| c == "system-messages")
77 {
78 doc.children.pop();
79 }
80}
81
82fn check_halt(doc: &Document, settings: &Settings) -> Result<(), Halt> {
83 let mut worst: Option<&crate::nodes::SystemMessage> = None;
84 for m in &doc.messages {
85 if settings.halt_on(m.level) && worst.map(|w| m.level >= w.level).unwrap_or(true) {
86 worst = Some(m);
87 }
88 }
89 fn walk(blocks: &[crate::nodes::Block], settings: &Settings, worst: &mut Option<String>) {
91 for b in blocks {
92 if let crate::nodes::Block::SystemMessage(m) = b
93 && settings.halt_on(m.level)
94 {
95 *worst = Some(m.text.clone());
96 }
97 match b {
98 crate::nodes::Block::Section(s) => walk(&s.children, settings, worst),
99 crate::nodes::Block::Topic(t) => walk(&t.children, settings, worst),
100 crate::nodes::Block::Container(c) => walk(&c.children, settings, worst),
101 _ => {}
102 }
103 }
104 }
105 let mut tree_worst: Option<String> = None;
106 walk(&doc.children, settings, &mut tree_worst);
107 match (worst, tree_worst) {
108 (Some(m), _) => Err(Halt {
109 level: m.level,
110 message: m.text.clone(),
111 }),
112 (None, Some(t)) => Err(Halt {
113 level: settings.halt_level,
114 message: t,
115 }),
116 (None, None) => Ok(()),
117 }
118}
119
120fn filter_messages(doc: &mut Document, report_level: u8) {
124 fn walk(blocks: &mut Vec<Block>, report_level: u8) {
125 let mut kept: Vec<Block> = Vec::with_capacity(blocks.len());
126 for b in blocks.drain(..) {
127 match b {
128 Block::SystemMessage(m) if m.level < report_level => {}
129 Block::Section(mut s) => {
130 walk(&mut s.children, report_level);
131 if s.classes.iter().any(|c| c == "system-messages")
134 && !s
135 .children
136 .iter()
137 .any(|c| matches!(c, Block::SystemMessage(_)))
138 {
139 continue;
140 }
141 kept.push(Block::Section(s));
142 }
143 Block::Topic(mut t) => {
144 walk(&mut t.children, report_level);
145 kept.push(Block::Topic(t));
146 }
147 Block::Container(mut c) => {
148 walk(&mut c.children, report_level);
149 kept.push(Block::Container(c));
150 }
151 Block::BulletList(mut l) => {
152 for item in &mut l.items {
153 walk(item, report_level);
154 }
155 kept.push(Block::BulletList(l));
156 }
157 Block::EnumList(mut l) => {
158 for item in &mut l.items {
159 walk(item, report_level);
160 }
161 kept.push(Block::EnumList(l));
162 }
163 Block::DefinitionList(mut items) => {
164 for it in &mut items {
165 walk(&mut it.definition, report_level);
166 }
167 kept.push(Block::DefinitionList(items));
168 }
169 Block::FieldList(mut fields) => {
170 for f in &mut fields {
171 walk(&mut f.body, report_level);
172 }
173 kept.push(Block::FieldList(fields));
174 }
175 Block::OptionList(mut groups) => {
176 for g in &mut groups {
177 walk(&mut g.body, report_level);
178 }
179 kept.push(Block::OptionList(groups));
180 }
181 Block::BlockQuote(mut q) => {
182 walk(&mut q.children, report_level);
183 kept.push(Block::BlockQuote(q));
184 }
185 Block::Admonition(mut a) => {
186 walk(&mut a.children, report_level);
187 kept.push(Block::Admonition(a));
188 }
189 Block::Footnote(mut f) => {
190 walk(&mut f.children, report_level);
191 kept.push(Block::Footnote(f));
192 }
193 Block::Table(mut t) => {
194 for row in t.head.iter_mut().chain(t.body.iter_mut()) {
195 for cell in row {
196 walk(&mut cell.blocks, report_level);
197 }
198 }
199 kept.push(Block::Table(t));
200 }
201 other => kept.push(other),
202 }
203 }
204 *blocks = kept;
205 }
206 walk(&mut doc.children, report_level);
207 doc.messages.retain(|m| m.level >= report_level);
208}