use base64::{engine::general_purpose::STANDARD as BASE64_STANDARD, Engine}; use quick_xml::{
events::{BytesEnd, BytesStart, BytesText, Event as XmlEvent},
Error as XmlWriterError, Writer as EventWriter,
}; use std::{
borrow::Cow,
io::{self, Write},
};
// If there are no more open tags then write the </plist> element ifself.stack.is_empty() { ifself.write_root_element { // We didn't tell the xml_writer about the <plist> tag so we'll skip telling it // about the </plist> tag as well. self.xml_writer
.get_mut()
.write_all(b"\n</plist>")
.map_err(error::from_io_without_position)?;
}
#[cfg(feature = "serde")] pub(crate) fn encode_data_base64(data: &[u8]) -> String { // Pre-allocate space for the base64 encoded data. let num_lines = (data.len() + DATA_MAX_LINE_BYTES - 1) / DATA_MAX_LINE_BYTES; let max_len = num_lines * (DATA_MAX_LINE_CHARS + 1);
letmut base64 = Vec::with_capacity(max_len);
write_data_base64(data, false, b'\t', 0, &mut base64).expect("writing to a vec cannot fail");
String::from_utf8(base64).expect("encoded base64 is ascii")
}
fn write_data_base64(
data: &[u8],
write_initial_newline: bool,
indent_char: u8,
indent_repeat: usize, mut writer: impl Write,
) -> io::Result<()> { // XML plist data elements are always formatted by apple tools as // <data> // AAAA..AA (68 characters per line) // </data> letmut encoded = [0; DATA_MAX_LINE_CHARS]; for (i, line) in data.chunks(DATA_MAX_LINE_BYTES).enumerate() { // Write newline if write_initial_newline || i > 0 {
writer.write_all(&[b'\n'])?;
}
// Write indent for _ in0..indent_repeat {
writer.write_all(&[indent_char])?;
}
// Write bytes let encoded_len = BASE64_STANDARD
.encode_slice(line, &mut encoded)
.expect("encoded base64 max line length is known");
writer.write_all(&encoded[..encoded_len])?;
}
Ok(())
}
#[cfg(test)] mod tests { use std::io::Cursor;
usesuper::*; usecrate::stream::Event;
#[test] fn streaming_parser() { let plist = [
Event::StartDictionary(None),
Event::String("Author".into()),
Event::String("William Shakespeare".into()),
Event::String("Lines".into()),
Event::StartArray(None),
Event::String("It is a tale told by an idiot,".into()),
Event::String("Full of sound and fury, signifying nothing.".into()),
Event::Data((0..128).collect::<Vec<_>>().into()),
Event::EndCollection,
Event::String("Death".into()),
Event::Integer(1564.into()),
Event::String("Height".into()),
Event::Real(1.60),
Event::String("Data".into()),
Event::Data(vec![0, 0, 0, 190, 0, 0, 0, 3, 0, 0, 0, 30, 0, 0, 0].into()),
Event::String("Birthdate".into()),
Event::Date(super::Date::from_xml_format("1981-05-16T11:32:06Z").unwrap()),
Event::String("Comment".into()),
Event::String("2 < 3".into()), // make sure characters are escaped
Event::String("BiggestNumber".into()),
Event::Integer(18446744073709551615u64.into()),
Event::String("SmallestNumber".into()),
Event::Integer((-9223372036854775808i64).into()),
Event::String("IsTrue".into()),
Event::Boolean(true),
Event::String("IsNotFalse".into()),
Event::Boolean(false),
Event::EndCollection,
];
let expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
<plist version=\"1.0\">
<dict>
\t<key>Author</key>
\t<string>William Shakespeare</string>
\t<key>Lines</key>
\t<array>
\t\t<string>It is a tale told by an idiot,</string>
\t\t<string>Full of sound and fury, signifying nothing.</string>
\t\t<data>
\t\tAAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEy
\t\tMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2Rl
\t\tZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn8=
\t\t</data>
\t</array>
\t<key>Death</key>
\t<integer>1564</integer>
\t<key>Height</key>
\t<real>1.6</real>
\t<key>Data</key>
\t<data>
\tAAAAvgAAAAMAAAAeAAAA
\t</data>
\t<key>Birthdate</key>
\t<date>1981-05-16T11:32:06Z</date>
\t<key>Comment</key>
\t<string>2 < 3</string>
\t<key>BiggestNumber</key>
\t<integer>18446744073709551615</integer>
\t<key>SmallestNumber</key>
\t<integer>-9223372036854775808</integer>
\t<key>IsTrue</key>
\t<true/>
\t<key>IsNotFalse</key>
\t<false/>
</dict>
</plist>";
let actual = events_to_xml(plist, XmlWriteOptions::default());
assert_eq!(actual, expected);
}
#[test] fn custom_indent_string() { let plist = [
Event::StartArray(None),
Event::String("It is a tale told by an idiot,".into()),
Event::String("Full of sound and fury, signifying nothing.".into()),
Event::EndCollection,
];
let expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
<plist version=\"1.0\">
<array>
...<string>It is a tale told by an idiot,</string>
...<string>Full of sound and fury, signifying nothing.</string>
</array>
</plist>";
let actual = events_to_xml(plist, XmlWriteOptions::default().indent(b'.', 3));
assert_eq!(actual, expected);
}
#[test] fn no_root() { let plist = [
Event::StartArray(None),
Event::String("It is a tale told by an idiot,".into()),
Event::String("Full of sound and fury, signifying nothing.".into()),
Event::EndCollection,
];
let expected = "<array>
\t<string>It is a tale told by an idiot,</string>
\t<string>Full of sound and fury, signifying nothing.</string>
</array>";
let actual = events_to_xml(plist, XmlWriteOptions::default().root_element(false));
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.