inline_xml/
fmt.rs

1// Copyright (C) 2023 Benjamin Stürz
2//
3// This program is free software: you can redistribute it and/or modify
4// it under the terms of the GNU General Public License as published by
5// the Free Software Foundation, either version 3 of the License, or
6// (at your option) any later version.
7//
8// This program is distributed in the hope that it will be useful,
9// but WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11// GNU General Public License for more details.
12//
13// You should have received a copy of the GNU General Public License
14// along with this program.  If not, see <http://www.gnu.org/licenses/>.
15use std::fmt::{self, Display, Formatter};
16use super::*;
17
18const INDENT: usize = 4;
19
20struct IndentedXml<'a>(&'a Xml, usize);
21struct IndentedTag<'a>(&'a Tag, usize);
22struct IndentedContent<'a>(&'a Content, usize);
23
24fn escape_attr(s: &str) -> String {
25    s.chars().fold(String::new(), |mut s, ch| {
26        match ch {
27            '\\' => s + "\\\\",
28            '\"' => s + "\\\"",
29            '\n' => s + "\\n",
30            _    => { s.push(ch); s },
31        }
32    })
33}
34
35impl Display for IndentedXml<'_> {
36    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
37        let Self(x, ind) = self;
38        let ind2 = ind + INDENT;
39        x.0.iter().try_for_each(|x| write!(f, "{}", IndentedContent(x, ind2)))
40    }
41}
42
43impl Display for IndentedTag<'_> {
44    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
45        let Self(tag, ind) = self;
46        let ind2 = ind + INDENT;
47        let n = &tag.name;
48        write!(f, "{:ind$}<{n}", "")?;
49        tag.attrs.iter().try_for_each(|a| write!(f, " {a}"))?;
50        if let Some(inner) = tag.inner.as_ref() {
51            match &inner.0[..] {
52                &[] => write!(f, "></{n}>"),
53                &[ref x] => {
54                    let sep = match x {
55                        Content::Tag(_)     => true,
56                        Content::Nested(_)  => true,
57                        Content::Word(_)    => false,
58                    };
59
60                    if sep {
61                        writeln!(f ,">")?;
62                        writeln!(f, "{}", IndentedContent(x, ind2))?;
63                        write!(f, "{:ind$}</{n}>", "")
64                    } else {
65                        write!(f, ">{x}</{n}>")
66                    }
67                },
68                data => {
69                    writeln!(f, ">")?;
70                    data.iter().try_for_each(|x| writeln!(f, "{}", IndentedContent(x, ind2)))?;
71                    write!(f, "{:ind$}</{n}>", "")
72                }
73            }
74        } else {
75            write!(f, " />")
76        }
77    }
78}
79
80impl Display for IndentedContent<'_> {
81    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
82        let Self(x, ind) = self;
83        match x {
84            Content::Tag(t)     => write!(f, "{}", IndentedTag(t, *ind)),
85            Content::Word(w)    => write!(f, "{:ind$}{}", "", w),
86            Content::Nested(x)  => write!(f, "{}", IndentedXml(x, *ind - INDENT)),
87        }
88    }
89}
90
91
92impl Display for Document {
93    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
94        if self.xml_decl_attrs.len() > 0 {
95            write!(f, "<?xml")?;
96            for (n, v) in &self.xml_decl_attrs {
97                write!(f, " {}=\"{}\"", n, escape_attr(v))?;
98            }
99            writeln!(f, "?>")?;
100        }
101        if let Some(doctype) = &self.doctype {
102            writeln!(f, "<!DOCTYPE {doctype}>")?;
103        }
104        write!(f, "{}", self.root)
105    }
106}
107
108impl Display for Xml {
109    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
110        self.0.iter().try_for_each(|x| write!(f, "{x}"))
111    }
112}
113
114impl Display for Tag {
115    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
116        write!(f, "{}", IndentedTag(self, 0))
117    }
118}
119
120impl Display for Attr {
121    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
122        write!(f, "{}=\"{}\"", self.name, escape_attr(&self.value))
123    }
124}
125
126impl Display for Content {
127    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
128        match self {
129            Self::Tag(t)    => write!(f, "{t}"),
130            Self::Word(w)   => write!(f, "{w}"),
131            Self::Nested(x) => write!(f, "{}", IndentedXml(x, 0)),
132        }
133    }
134}
135