1use core::fmt;
2
3use askama_escape::{escape, Html as AskamaHtml};
4use stylish_core::{Style, Write};
5
6use crate::util;
7
8#[derive(Clone, Debug, Default)]
21pub struct Html<T: core::fmt::Write> {
22 inner: T,
23 current: Style,
24}
25
26impl<T: core::fmt::Write> Html<T> {
27 pub fn new(inner: T) -> Self {
29 Self {
30 inner,
31 current: Style::default(),
32 }
33 }
34
35 pub fn write_fmt(&mut self, args: stylish_core::Arguments<'_>) -> fmt::Result {
39 stylish_core::Write::write_fmt(self, args)
40 }
41
42 pub fn finish(mut self) -> Result<T, fmt::Error> {
46 if self.current != Style::default() {
47 self.inner.write_str("</span>")?;
48 }
49 Ok(self.inner)
50 }
51}
52
53impl<T: fmt::Write> Write for Html<T> {
54 fn write_str(&mut self, s: &str, style: Style) -> fmt::Result {
55 if s.is_empty() {
56 return Ok(());
57 }
58
59 if style == Style::default() {
60 if self.current != Style::default() {
61 self.inner.write_str("</span>")?;
62 }
63 } else if style != self.current {
64 let diff = style.diff_from(Style::default());
65 let segments = [
66 diff.foreground.map(util::foreground),
67 diff.background.map(util::background),
68 diff.intensity.map(util::intensity),
69 ];
70 let mut segments = segments.iter().filter_map(|&s| s);
71 if let Some(segment) = segments.next() {
72 if self.current != Style::default() {
73 self.inner.write_str("</span>")?;
74 }
75 self.inner.write_str("<span style=")?;
76 self.inner.write_str(segment)?;
77 for segment in segments {
78 self.inner.write_str(";")?;
79 self.inner.write_str(segment)?;
80 }
81 self.inner.write_str(">")?;
82 }
83 }
84 self.current = style;
85
86 write!(self.inner, "{}", escape(s, AskamaHtml))?;
87
88 Ok(())
89 }
90}