1use std::io::Write;
2
3use anyhow::{Error, bail};
4
5use super::{HTMLRender, HTMLRenderer, escape_html};
7use document_tree::{
8 Element, ExtraAttributes, HasChildren, attribute_types as at, element_categories as c,
9 elements as e, extra_attributes as a,
10};
11
12macro_rules! impl_html_render_cat {($cat:ident { $($member:ident),+ }) => {
15 impl HTMLRender for c::$cat {
16 fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write {
17 match self {$(
18 c::$cat::$member(elem) => elem.render_html(renderer),
19 )+}
20 }
21 }
22}}
23
24macro_rules! impl_html_render_simple {
25 (
26 $type1:ident => $tag1:ident,
27 $( $type:ident => $tag:ident ),+
28 ) => {
29 impl_html_render_simple!($type1 => $tag1);
30 $( impl_html_render_simple!($type => $tag); )+
31 };
32 ( $type:ident => $tag:ident ) => {
33 impl HTMLRender for e::$type {
34 fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write {
35 write!(renderer.stream, "<{}", stringify!($tag))?;
36 if self.classes().len() > 0 {
37 write!(renderer.stream, " class=\"{}\"", self.classes().join(" "))?;
38 }
39 write!(renderer.stream, ">")?;
40 self.children().render_html(renderer)?;
41 write!(renderer.stream, "</{}>", stringify!($tag))?;
42 Ok(())
43 }
44 }
45 };
46}
47
48macro_rules! impl_html_render_simple_nochildren {( $($type:ident => $tag:ident),+ ) => { $(
49 impl HTMLRender for e::$type {
50 fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write {
51 write!(renderer.stream, "<{0}></{0}>", stringify!($tag))?;
52 Ok(())
53 }
54 }
55)+ }}
56
57impl_html_render_cat!(StructuralSubElement {
60 Title,
61 Subtitle,
62 Decoration,
63 Docinfo,
64 SubStructure
65});
66impl_html_render_simple!(Subtitle => h2);
67
68impl HTMLRender for e::Title {
69 fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
70 where
71 W: Write,
72 {
73 let level = if renderer.level > 6 {
74 6
75 } else {
76 renderer.level
77 };
78 write!(renderer.stream, "<h{level}>")?;
79 self.children().render_html(renderer)?;
80 write!(renderer.stream, "</h{level}>")?;
81 Ok(())
82 }
83}
84
85impl HTMLRender for e::Docinfo {
86 fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
87 where
88 W: Write,
89 {
90 unimplemented!();
92 }
93}
94
95impl HTMLRender for e::Decoration {
96 fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
97 where
98 W: Write,
99 {
100 unimplemented!();
102 }
103}
104
105impl_html_render_cat!(SubStructure {
106 Topic,
107 Sidebar,
108 Transition,
109 Section,
110 BodyElement
111});
112impl_html_render_simple!(Sidebar => aside);
113
114impl HTMLRender for e::Section {
115 fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
116 where
117 W: Write,
118 {
119 renderer.level += 1;
120 write!(renderer.stream, "<section id=\"{0}\">", self.ids()[0].0)?;
121 self.children().render_html(renderer)?;
122 write!(renderer.stream, "</section>")?;
123 renderer.level -= 1;
124 Ok(())
125 }
126}
127
128impl HTMLRender for e::Transition {
129 fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
130 where
131 W: Write,
132 {
133 write!(renderer.stream, "<hr/>")?;
134 Ok(())
135 }
136}
137
138impl HTMLRender for e::Topic {
139 fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
140 where
141 W: Write,
142 {
143 unimplemented!();
145 }
146}
147
148impl_html_render_cat!(BodyElement {
149 Paragraph,
150 LiteralBlock,
151 DoctestBlock,
152 MathBlock,
153 Rubric,
154 SubstitutionDefinition,
155 Comment,
156 Pending,
157 Target,
158 Raw,
159 Image,
160 Compound,
161 Container,
162 BulletList,
163 EnumeratedList,
164 DefinitionList,
165 FieldList,
166 OptionList,
167 LineBlock,
168 BlockQuote,
169 Admonition,
170 Attention,
171 Hint,
172 Note,
173 Caution,
174 Danger,
175 Error,
176 Important,
177 Tip,
178 Warning,
179 Footnote,
180 Citation,
181 SystemMessage,
182 Figure,
183 Table
184});
185impl_html_render_simple!(Paragraph => p, MathBlock => math, Rubric => a, Compound => p, Container => div, BulletList => ul, EnumeratedList => ol, DefinitionList => dl, FieldList => dl, OptionList => pre, LineBlock => div, BlockQuote => blockquote, Admonition => aside, Attention => aside, Hint => aside, Note => aside, Caution => aside, Danger => aside, Error => aside, Important => aside, Tip => aside, Warning => aside, Figure => figure);
186impl_html_render_simple_nochildren!(Table => table); trait IMark {}
190impl IMark for e::Image {}
191impl IMark for e::ImageInline {}
192impl<I> HTMLRender for I
193where
194 I: e::Element + a::ExtraAttributes<a::Image> + IMark,
195{
196 fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
197 where
198 W: Write,
199 {
200 let extra = self.extra();
201 if let Some(target) = extra.target.as_ref() {
202 write!(
203 renderer.stream,
204 "<a href=\"{}\">",
205 escape_html(target.as_str())
206 )?;
207 }
208 write!(renderer.stream, "<img")?;
209 if let Some(alt) = extra.alt.as_ref() {
210 write!(renderer.stream, " alt=\"{}\"", escape_html(alt))?;
211 }
212 write!(
217 renderer.stream,
218 " src=\"{}\" />",
219 escape_html(extra.uri.as_str())
220 )?;
221 if extra.target.is_some() {
222 write!(renderer.stream, "</a>")?;
223 }
224 Ok(())
225 }
226}
227
228impl HTMLRender for e::LiteralBlock {
229 fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
230 where
231 W: Write,
232 {
233 let mut cls_iter = self.classes().iter();
234 let is_code = cls_iter.next() == Some(&"code".to_owned());
235 write!(renderer.stream, "<pre>")?;
236 if is_code {
237 if let Some(lang) = cls_iter.next() {
239 write!(renderer.stream, "<code class=\"language-{lang}\">")?;
240 } else {
241 write!(renderer.stream, "<code>")?;
242 }
243 }
244 self.children().render_html(renderer)?;
245 if is_code {
246 write!(renderer.stream, "</code>")?;
247 }
248 write!(renderer.stream, "</pre>")?;
249 Ok(())
250 }
251}
252
253impl HTMLRender for e::DoctestBlock {
254 fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
255 where
256 W: Write,
257 {
258 unimplemented!();
260 }
261}
262
263impl HTMLRender for e::SubstitutionDefinition {
264 fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
265 where
266 W: Write,
267 {
268 Ok(())
270 }
271}
272
273impl HTMLRender for e::Comment {
274 fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
275 where
276 W: Write,
277 {
278 write!(renderer.stream, "<!--")?;
279 self.children().render_html(renderer)?;
280 write!(renderer.stream, "-->")?;
281 Ok(())
282 }
283}
284
285impl HTMLRender for e::Pending {
286 fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
287 where
288 W: Write,
289 {
290 unimplemented!();
292 }
293}
294
295impl HTMLRender for e::Target {
296 fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
297 where
298 W: Write,
299 {
300 Ok(())
302 }
303}
304
305impl HTMLRender for e::Raw {
306 fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
307 where
308 W: Write,
309 {
310 let extra = self.extra();
311 if extra.format.contains(&at::NameToken("html".to_owned())) {
312 for c in self.children() {
313 write!(renderer.stream, "{c}")?;
314 }
315 }
316 Ok(())
317 }
318}
319
320impl HTMLRender for e::Footnote {
321 fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
322 where
323 W: Write,
324 {
325 use c::SubFootnote::BodyElement;
326
327 let mut children = self.children().iter();
328 match (self.get_label(), self.extra().auto) {
329 (Ok(label), Some(at::AutoFootnoteType::Symbol)) => {
330 children.next(); write!(renderer.stream, "<li value=\"{label}\" class=\"symbol\">")?;
332 }
333 (Ok(label), _) => {
334 children.next(); write!(renderer.stream, "<li value=\"{label}\">")?;
336 }
337 (Err(_), _) => {
338 write!(renderer.stream, "<li>")?;
339 }
340 }
341 for child in children {
342 let BodyElement(child) = child else {
343 bail!("Cannot have a footnote label anywhere but as first child node");
344 };
345 child.render_html(renderer)?;
346 }
347 write!(renderer.stream, "</li>")?;
348 Ok(())
349 }
350}
351
352impl HTMLRender for e::Citation {
353 fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
354 where
355 W: Write,
356 {
357 unimplemented!();
358 }
359}
360
361impl HTMLRender for e::SystemMessage {
362 fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
363 where
364 W: Write,
365 {
366 write!(renderer.stream, "<figure><caption>System Message</caption>")?;
367 self.children().render_html(renderer)?;
368 write!(renderer.stream, "</figure>")?;
369 Ok(())
370 }
371}
372
373impl_html_render_cat!(TextOrInlineElement {
374 String,
375 Emphasis,
376 Strong,
377 Literal,
378 Reference,
379 FootnoteReference,
380 CitationReference,
381 SubstitutionReference,
382 TitleReference,
383 Abbreviation,
384 Acronym,
385 Superscript,
386 Subscript,
387 Inline,
388 Problematic,
389 Generated,
390 Math,
391 TargetInline,
392 RawInline,
393 ImageInline
394});
395impl_html_render_simple!(Emphasis => em, Strong => strong, Literal => code, FootnoteReference => a, CitationReference => a, TitleReference => a, Abbreviation => abbr, Acronym => acronym, Superscript => sup, Subscript => sub, Inline => span, Math => math, TargetInline => a);
396
397impl HTMLRender for String {
398 fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
399 where
400 W: Write,
401 {
402 write!(renderer.stream, "{}", escape_html(self))?;
403 Ok(())
404 }
405}
406
407impl HTMLRender for e::Reference {
408 fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
409 where
410 W: Write,
411 {
412 let extra = self.extra();
413 write!(renderer.stream, "<a")?;
414 if let Some(target) = extra.refuri.as_ref() {
415 write!(
416 renderer.stream,
417 " href=\"{}\"",
418 escape_html(target.as_str())
419 )?;
420 }
421 write!(renderer.stream, ">")?;
427 self.children().render_html(renderer)?;
428 write!(renderer.stream, "</a>")?;
429 Ok(())
430 }
431}
432
433impl HTMLRender for e::SubstitutionReference {
434 fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
435 where
436 W: Write,
437 {
438 unimplemented!();
440 }
441}
442
443impl HTMLRender for e::Problematic {
444 fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
445 where
446 W: Write,
447 {
448 unimplemented!();
450 }
451}
452
453impl HTMLRender for e::Generated {
454 fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
455 where
456 W: Write,
457 {
458 unimplemented!();
460 }
461}
462
463impl HTMLRender for e::RawInline {
464 fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
465 where
466 W: Write,
467 {
468 self.children().render_html(renderer)
469 }
470}
471
472impl_html_render_cat!(SubTopic { Title, BodyElement });
477impl_html_render_cat!(SubSidebar {
478 Topic,
479 Title,
480 Subtitle,
481 BodyElement
482});
483impl_html_render_simple!(ListItem => li);
484
485impl HTMLRender for e::DefinitionListItem {
486 fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
487 where
488 W: Write,
489 {
490 unimplemented!();
492 }
493}
494
495impl HTMLRender for e::Field {
496 fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
497 where
498 W: Write,
499 {
500 unimplemented!();
502 }
503}
504
505impl HTMLRender for e::OptionListItem {
506 fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
507 where
508 W: Write,
509 {
510 unimplemented!();
512 }
513}
514
515impl_html_render_cat!(SubLineBlock { LineBlock, Line });
516
517impl HTMLRender for e::Line {
518 fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
519 where
520 W: Write,
521 {
522 self.children().render_html(renderer)?;
523 write!(renderer.stream, "<br>")?;
524 Ok(())
525 }
526}
527
528impl_html_render_cat!(SubBlockQuote {
529 Attribution,
530 BodyElement
531});
532impl_html_render_simple!(Attribution => cite); impl_html_render_cat!(SubFigure {
535 Caption,
536 Legend,
537 BodyElement
538});
539impl_html_render_simple!(Caption => caption);
540
541impl HTMLRender for e::Legend {
542 fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
543 where
544 W: Write,
545 {
546 unimplemented!();
547 }
548}