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