domafic/
html_writer.rs

1extern crate marksman_escape;
2use self::marksman_escape::Escape;
3
4use {DomNode, DomNodes, DomValue};
5use processors::DomNodeProcessor;
6
7// This module as a whole is "use_std"-only, so these don't need to be cfg'd
8use std::marker::PhantomData;
9use std::fmt;
10use std::io;
11
12/// Type to use for processing a `DomNode` tree and writing it to HTML.
13///
14/// This type should not ever need to be instantiated. Instead, simply
15/// name the type in calls to `DomNodes::process_all::<HtmlWriter<...>>(...)`.
16#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
17pub struct HtmlWriter<W: io::Write>(PhantomData<W>);
18impl<'a, M, W: io::Write> DomNodeProcessor<'a, M> for HtmlWriter<W> {
19    type Acc = W;
20    type Error = io::Error;
21
22    fn get_processor<T: DomNode<M>>() -> fn(&mut Self::Acc, &T) -> Result<(), Self::Error> {
23        fn add_node<M, W, T>(w: &mut W, node: &T) -> Result<(), io::Error>
24                where W: io::Write, T: DomNode<M> {
25            match node.value() {
26                DomValue::Element { tag: tagname } => {
27                    write!(w, "<{}", tagname)?;
28                    for attr in node.attributes() {
29                        write!(w, " {}=\"{}\"", attr.0, attr.1)?;
30                    }
31                    write!(w, ">")?;
32                    node.children().process_all::<HtmlWriter<W>>(w)?;
33                    write!(w, "</{}>", tagname)
34                }
35                DomValue::Text(text) => {
36                    for escaped_u8 in Escape::new(text.bytes()) {
37                        w.write(&[escaped_u8])?;
38                    }
39                    Ok(())
40                }
41            }
42        }
43        add_node
44    }
45}
46
47/// Wrapper struct to allow `DomNode`s to implement `Display` as html
48#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
49pub struct HtmlDisplayable<'a, M, T: DomNode<M> + 'a>(pub &'a T, pub PhantomData<M>);
50
51impl<'a, M, T: DomNode<M>> fmt::Display for HtmlDisplayable<'a, M, T> {
52    fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
53        // TODO the extra string allocation here is almost certainly avoidable
54        let mut string_buffer = Vec::new();
55        self.0.write_html(&mut string_buffer)
56            .map_err(|_| fmt::Error)?;
57        let string = String::from_utf8(string_buffer)
58            .map_err(|_| fmt::Error)?;
59        formatter.write_str(&string)
60    }
61}