display_as/
html.rs

1//! [Format] as HTML
2
3use super::*;
4
5/// [Format] as HTML.
6#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
7pub struct HTML;
8impl Format for HTML {
9    fn escape(f: &mut Formatter, mut s: &str) -> Result<(), Error> {
10        let badstuff = "<>&\"'/";
11        while let Some(idx) = s.find(|c| badstuff.contains(c)) {
12            let (first, rest) = s.split_at(idx);
13            let (badchar, tail) = rest.split_at(1);
14            f.write_str(first)?;
15            f.write_str(match badchar {
16                "<" => "&lt;",
17                ">" => "&gt;",
18                "&" => "&amp;",
19                "\"" => "&quot;",
20                "'" => "&#x27;",
21                "/" => "&#x2f;",
22                _ => unreachable!(),
23            })?;
24            s = tail;
25        }
26        f.write_str(s)
27    }
28    /// The MIME type for HTML is [mime::TEXT_HTML_UTF_8].
29    fn mime() -> mime::Mime {
30        return mime::TEXT_HTML_UTF_8;
31    }
32    fn this_format() -> Self {
33        HTML
34    }
35}
36
37macro_rules! display_as_from_display {
38    ($format:ty, $type:ty) => {
39        impl DisplayAs<$format> for $type {
40            fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
41                (&self as &dyn Display).fmt(f)
42            }
43        }
44    };
45}
46
47/// Conveniently implement [DisplayAs] for integers for a new [Format].
48#[macro_export]
49macro_rules! display_integers_as {
50    ($format:ty) => {
51        display_as_from_display!($format, i8);
52        display_as_from_display!($format, u8);
53        display_as_from_display!($format, i16);
54        display_as_from_display!($format, u16);
55        display_as_from_display!($format, i32);
56        display_as_from_display!($format, u32);
57        display_as_from_display!($format, i64);
58        display_as_from_display!($format, u64);
59        display_as_from_display!($format, i128);
60        display_as_from_display!($format, u128);
61        display_as_from_display!($format, isize);
62        display_as_from_display!($format, usize);
63    };
64}
65
66display_integers_as!(HTML);
67
68/// Inconveniently implement [DisplayAs] for floats for a new [Format].
69///
70/// This is inconvenient because we want to enable pretty formatting
71/// of both large and small numbers in whatever markup language we are
72/// using.  The first argument of the macro is the format that wants
73/// implementation of [DisplayAs] for floats.
74///
75/// For partial documentation of the other files, see
76/// [Floating::fmt_with](float/enum.Floating.html#method.fmt_with).
77/// However, I think some examples for HTML will most easily define
78/// the other arguments.
79/// ```
80/// #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
81/// struct HTML;
82/// use display_as::{Format, format_as};
83/// impl Format for HTML {
84///    fn escape(f: &mut ::std::fmt::Formatter, mut s: &str) -> Result<(), ::std::fmt::Error> {
85///        f.write_str(s) // for example I skip escaping...
86///    }
87///    fn mime() -> mime::Mime { return mime::TEXT_HTML_UTF_8; }
88///    fn this_format() -> Self { HTML }
89/// }
90/// display_as::display_floats_as!(HTML, "×10<sup>", "</sup>", 3, Some("10<sup>"));
91/// fn main() {
92///   assert_eq!(&format_as!(HTML, 1e3).into_string(), "1000");
93///   assert_eq!(&format_as!(HTML, 3e4).into_string(), "30000");
94///   assert_eq!(&format_as!(HTML, 1e5).into_string(), "10<sup>5</sup>");
95///   assert_eq!(&format_as!(HTML, 2e5).into_string(), "2×10<sup>5</sup>");
96///   assert_eq!(&format_as!(HTML, 1e6).into_string(), "10<sup>6</sup>");
97/// }
98/// ```
99#[macro_export]
100macro_rules! display_floats_as {
101    ($format:ty, $e:expr, $after_e:expr, $e_cost:expr, $power_ten:expr) => {
102        impl $crate::DisplayAs<$format> for f64 {
103            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
104                $crate::float::Floating::from(*self).fmt_with(f, $e, $after_e, $e_cost, $power_ten)
105            }
106        }
107        impl $crate::DisplayAs<$format> for f32 {
108            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
109                $crate::float::Floating::from(*self).fmt_with(f, $e, $after_e, $e_cost, $power_ten)
110            }
111        }
112    };
113}
114display_floats_as!(HTML, "×10<sup>", "</sup>", 3, Some("10<sup>"));
115
116#[test]
117fn escaping() {
118    assert_eq!(&format_as!(HTML, ("&")).into_string(), "&amp;");
119    assert_eq!(
120        &format_as!(HTML, ("hello &>this is cool")).into_string(),
121        "hello &amp;&gt;this is cool"
122    );
123    assert_eq!(
124        &format_as!(HTML, ("hello &>this is 'cool")).into_string(),
125        "hello &amp;&gt;this is &#x27;cool"
126    );
127}
128#[test]
129fn floats() {
130    assert_eq!(&format_as!(HTML, 3.0).into_string(), "3");
131    assert_eq!(&format_as!(HTML, 3e5).into_string(), "3×10<sup>5</sup>");
132    assert_eq!(&format_as!(HTML, 1e-6).into_string(), "10<sup>-6</sup>");
133    assert_eq!(&format_as!(HTML, 3e4).into_string(), "30000");
134}