/// An encoding of a plist as a flat structure. /// /// Output by the event readers. /// /// Dictionary keys and values are represented as pairs of values e.g.: /// /// ```ignore rust /// StartDictionary /// String("Height") // Key /// Real(181.2) // Value /// String("Age") // Key /// Integer(28) // Value /// EndDictionary /// ``` /// /// ## Lifetimes /// /// This type has a lifetime parameter; during serialization, data is borrowed /// from a [`Value`], and the lifetime of the event is the lifetime of the /// [`Value`] being serialized. /// /// During deserialization, data is always copied anyway, and this lifetime /// is always `'static`. #[derive(Clone, Debug, PartialEq)] #[non_exhaustive] pubenum Event<'a> { // While the length of an array or dict cannot be feasably greater than max(usize) this better // conveys the concept of an effectively unbounded event stream.
StartArray(Option<u64>),
StartDictionary(Option<u64>),
EndCollection,
/// An owned [`Event`]. /// /// During deserialization, events are always owned; this type alias helps /// keep that code a bit clearer. pubtype OwnedEvent = Event<'static>;
/// An `Event` stream returned by `Value::into_events`. pubstruct Events<'a> {
stack: Vec<StackItem<'a>>,
}
/// Options for customizing serialization of XML plists. #[derive(Clone, Debug)] pubstruct XmlWriteOptions {
root_element: bool,
indent_char: u8,
indent_count: usize,
}
impl XmlWriteOptions { /// Specify the sequence of characters used for indentation. /// /// This may be either an `&'static str` or an owned `String`. /// /// The default is `\t`. /// /// Since replacing `xml-rs` with `quick-xml`, the indent string has to consist of a single /// repeating ascii character. This is a backwards compatibility function, prefer using /// [`XmlWriteOptions::indent`]. #[deprecated(since = "1.4.0", note = "please use `indent` instead")] pubfn indent_string(self, indent_str: impl Into<Cow<'static, str>>) -> Self { let indent_str = indent_str.into(); let indent_str = indent_str.as_ref();
if indent_str.is_empty() { returnself.indent(0, 0);
}
assert!(
indent_str.chars().all(|chr| chr.is_ascii()), "indent str must be ascii"
); let indent_str = indent_str.as_bytes();
assert!(
indent_str.iter().all(|chr| chr == &indent_str[0]), "indent str must consist of a single repeating character"
);
self.indent(indent_str[0], indent_str.len())
}
/// Specifies the character and amount used for indentation. /// /// `indent_char` must be a valid UTF8 character. /// /// The default is indenting with a single tab. pubfn indent(mutself, indent_char: u8, indent_count: usize) -> Self { self.indent_char = indent_char; self.indent_count = indent_count; self
}
/// Selects whether to write the XML prologue, plist document type and root element. /// /// In other words the following: /// ```xml /// <?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"> /// ... /// </plist> /// ``` /// /// The default is `true`. pubfn root_element(mutself, write_root: bool) -> Self { self.root_element = write_root; self
}
}
impl<'a> Iterator for Events<'a> { type Item = Event<'a>;
fn next(&mutself) -> Option<Event<'a>> { fn handle_value<'c, 'b: 'c>(
value: &'b Value,
stack: &'c mut Vec<StackItem<'b>>,
) -> Event<'b> { match value {
Value::Array(array) => { let len = array.len(); let iter = array.iter();
stack.push(StackItem::Array(iter));
Event::StartArray(Some(len as u64))
}
Value::Dictionary(dict) => { let len = dict.len(); let iter = dict.into_iter();
stack.push(StackItem::Dict(iter));
Event::StartDictionary(Some(len as u64))
}
Value::Boolean(value) => Event::Boolean(*value),
Value::Data(value) => Event::Data(Cow::Borrowed(value)),
Value::Date(value) => Event::Date(*value),
Value::Real(value) => Event::Real(*value),
Value::Integer(value) => Event::Integer(*value),
Value::String(value) => Event::String(Cow::Borrowed(value.as_str())),
Value::Uid(value) => Event::Uid(*value),
}
}
Some(matchself.stack.pop()? {
StackItem::Root(value) => handle_value(value, &mutself.stack),
StackItem::Array(mut array) => { iflet Some(value) = array.next() { // There might still be more items in the array so return it to the stack. self.stack.push(StackItem::Array(array));
handle_value(value, &mutself.stack)
} else {
Event::EndCollection
}
}
StackItem::Dict(mut dict) => { iflet Some((key, value)) = dict.next() { // There might still be more items in the dictionary so return it to the stack. self.stack.push(StackItem::Dict(dict)); // The next event to be returned must be the dictionary value. self.stack.push(StackItem::DictValue(value)); // Return the key event now.
Event::String(Cow::Borrowed(key))
} else {
Event::EndCollection
}
}
StackItem::DictValue(value) => handle_value(value, &mutself.stack),
})
}
}
fn init(&mutself, mut reader: R) -> Result<Option<OwnedEvent>, Error> { // Rewind reader back to the start. iflet Err(err) = reader.rewind().map_err(from_io_offset_0) { self.0 = ReaderInner::Uninitialized(Some(reader)); return Err(err);
}
// A plist is binary if it starts with magic bytes. match Reader::is_binary(&mut reader) {
Ok(true) => { self.0 = ReaderInner::Binary(BinaryReader::new(reader)); returnself.next().transpose();
}
Ok(false) => (),
Err(err) => { self.0 = ReaderInner::Uninitialized(Some(reader)); return Err(err);
}
};
// If a plist is not binary, try to parse as XML. // Use a `BufReader` for XML and ASCII plists as it is required by `quick-xml` and will // definitely speed up ASCII parsing as well. letmut xml_reader = XmlReader::new(BufReader::new(reader)); letmut reader = match xml_reader.next() {
res @ Some(Ok(_)) | res @ None => { self.0 = ReaderInner::Xml(xml_reader); return res.transpose();
}
Some(Err(err)) if xml_reader.xml_doc_started() => { self.0 = ReaderInner::Uninitialized(Some(xml_reader.into_inner().into_inner())); return Err(err);
}
Some(Err(_)) => xml_reader.into_inner(),
};
// Rewind reader back to the start. iflet Err(err) = reader.rewind().map_err(from_io_offset_0) { self.0 = ReaderInner::Uninitialized(Some(reader.into_inner())); return Err(err);
}
// If no valid XML markup is found, try to parse as ASCII. letmut ascii_reader = AsciiReader::new(reader); match ascii_reader.next() {
res @ Some(Ok(_)) | res @ None => { self.0 = ReaderInner::Ascii(ascii_reader);
res.transpose()
}
Some(Err(err)) => { self.0 = ReaderInner::Uninitialized(Some(ascii_reader.into_inner().into_inner()));
Err(err)
}
}
}
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.