diff options
Diffstat (limited to 'third_party/rust/embed-manifest/src/manifest/xml.rs')
-rw-r--r-- | third_party/rust/embed-manifest/src/manifest/xml.rs | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/third_party/rust/embed-manifest/src/manifest/xml.rs b/third_party/rust/embed-manifest/src/manifest/xml.rs new file mode 100644 index 0000000000..a247f42a61 --- /dev/null +++ b/third_party/rust/embed-manifest/src/manifest/xml.rs @@ -0,0 +1,140 @@ +use std::fmt::{self, Display, Formatter, Write}; + +/// Simple but still over-engineered XML generator for a [`Formatter`], for generating +/// Windows Manifest XML. This can easily generate invalid XML. +/// +/// When used correctly, this should generate the same output as MT’s `-canonicalize` +/// option. +pub struct XmlFormatter<'a, 'f> { + f: &'f mut Formatter<'a>, + state: State, + depth: usize, +} + +#[derive(Eq, PartialEq)] +enum State { + Init, + StartTag, + Text, +} + +impl<'a, 'f> XmlFormatter<'a, 'f> { + pub fn new(f: &'f mut Formatter<'a>) -> Self { + Self { + f, + state: State::Init, + depth: 0, + } + } + + fn pretty(&mut self) -> fmt::Result { + if self.f.alternate() { + self.f.write_str("\r\n")?; + for _ in 0..self.depth { + self.f.write_str(" ")?; + } + } + Ok(()) + } + + pub fn start_document(&mut self) -> fmt::Result { + if !self.f.alternate() { + self.f.write_char('\u{FEFF}')?; + } + self.f + .write_str("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\r\n") + } + + pub fn element<F: FnOnce(&mut Self) -> fmt::Result>(&mut self, name: &str, attrs: &[(&str, &str)], f: F) -> fmt::Result { + self.start_element(name, attrs)?; + f(self)?; + self.end_element(name) + } + + pub fn empty_element(&mut self, name: &str, attrs: &[(&str, &str)]) -> fmt::Result { + self.start_element(name, attrs)?; + self.end_element(name) + } + + pub fn start_element(&mut self, name: &str, attrs: &[(&str, &str)]) -> fmt::Result { + if self.state == State::StartTag { + self.f.write_char('>')?; + } + if self.depth != 0 { + self.pretty()?; + } + write!(self.f, "<{}", name)?; + for (name, value) in attrs { + write!(self.f, " {}=\"{}\"", name, Xml(value))?; + } + self.depth += 1; + self.state = State::StartTag; + Ok(()) + } + + pub fn end_element(&mut self, name: &str) -> fmt::Result { + self.depth -= 1; + match self.state { + State::Init => { + self.pretty()?; + write!(self.f, "</{}>", name) + } + State::Text => { + self.state = State::Init; + write!(self.f, "</{}>", name) + } + State::StartTag => { + self.state = State::Init; + if self.f.alternate() { + self.f.write_str("/>") + } else { + write!(self.f, "></{}>", name) + } + } + } + } + + pub fn text(&mut self, s: &str) -> fmt::Result { + if self.state == State::StartTag { + self.state = State::Text; + self.f.write_char('>')?; + } + Xml(s).fmt(self.f) + } +} + +/// Temporary wrapper for outputting a string with XML attribute encoding. +/// This does not do anything with the control characters which are not +/// valid in XML, encoded or not. +struct Xml<'a>(&'a str); + +impl<'a> Display for Xml<'a> { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + // Process the string in blocks separated by special characters, so that the parts that + // don't need encoding can be written all at once, not character by character, and with + // no checks for whether string slices are aligned on character boundaries. + for s in self.0.split_inclusive(&['<', '&', '>', '"', '\r'][..]) { + // Check whether the last character in the substring needs encoding. This will be + // `None` at the end of the input string. + let mut iter = s.chars(); + let ch = match iter.next_back() { + Some('<') => Some("<"), + Some('&') => Some("&"), + Some('>') => Some(">"), + Some('"') => Some("""), + Some('\r') => Some(" "), + _ => None, + }; + // Write the substring except the last character, then the encoded character; + // or the entire substring if it is not terminated by a special character. + match ch { + Some(enc) => { + f.write_str(iter.as_str())?; + f.write_str(enc)?; + } + None => f.write_str(s)?, + } + } + Ok(()) + } +} |