/// A YAML node is stored as this `Yaml` enumeration, which provides an easy way to /// access your YAML document. /// /// # Examples /// /// ``` /// use yaml_rust2::Yaml; /// let foo = Yaml::from_str("-123"); // convert the string to the appropriate YAML type /// assert_eq!(foo.as_i64().unwrap(), -123); /// /// // iterate over an Array /// let vec = Yaml::Array(vec![Yaml::Integer(1), Yaml::Integer(2)]); /// for v in vec.as_vec().unwrap() { /// assert!(v.as_i64().is_some()); /// } /// ``` #[derive(Clone, PartialEq, PartialOrd, Debug, Eq, Ord, Hash)] pubenum Yaml { /// Float types are stored as String and parsed on demand. /// Note that `f64` does NOT implement Eq trait and can NOT be stored in `BTreeMap`.
Real(String), /// YAML int is stored as i64.
Integer(i64), /// YAML scalar.
String(String), /// YAML bool, e.g. `true` or `false`.
Boolean(bool), /// YAML array, can be accessed as a [`Vec`].
Array(Array), /// YAML hash, can be accessed as a [`LinkedHashMap`]. /// /// Insertion order will match the order of insertion into the map.
Hash(Hash), /// Alias, not fully supported yet.
Alias(usize), /// YAML null, e.g. `null` or `~`.
Null, /// Accessing a nonexistent node via the Index trait returns `BadValue`. This /// simplifies error handling in the calling code. Invalid type conversion also /// returns `BadValue`.
BadValue,
}
/// The type contained in the `Yaml::Array` variant. This corresponds to YAML sequences. pubtype Array = Vec<Yaml>; /// The type contained in the `Yaml::Hash` variant. This corresponds to YAML mappings. pubtype Hash = LinkedHashMap<Yaml, Yaml>;
// parse f64 as Core schema // See: https://github.com/chyh1990/yaml-rust/issues/51 fn parse_f64(v: &str) -> Option<f64> { match v { ".inf" | ".Inf" | ".INF" | "+.inf" | "+.Inf" | "+.INF" => Some(f64::INFINITY), "-.inf" | "-.Inf" | "-.INF" => Some(f64::NEG_INFINITY), ".nan" | ".NaN" | ".NAN" => Some(f64::NAN), // Test that `v` contains a digit so as not to pass in strings like `inf`, // which rust will parse as a float
_ if v.as_bytes().iter().any(u8::is_ascii_digit) => v.parse::<f64>().ok(),
_ => None,
}
}
/// Main structure for quickly parsing YAML. /// /// See [`YamlLoader::load_from_str`]. #[derive(Default)] pubstruct YamlLoader { /// The different YAML documents that are loaded.
docs: Vec<Yaml>, // states // (current node, anchor_id) tuple
doc_stack: Vec<(Yaml, usize)>,
key_stack: Vec<Yaml>,
anchor_map: BTreeMap<usize, Yaml>, /// An error, if one was encountered.
error: Option<ScanError>,
}
/// An error that happened when loading a YAML document. #[derive(Debug)] pubenum LoadError { /// An I/O error.
IO(std::io::Error), /// An error within the scanner. This indicates a malformed YAML input.
Scan(ScanError), /// A decoding error (e.g.: Invalid UTF-8).
Decode(std::borrow::Cow<'static, str>),
}
fn insert_new_node(&mutself, node: (Yaml, usize), mark: Marker) -> Result<(), ScanError> { // valid anchor id starts from 1 if node.1 > 0 { self.anchor_map.insert(node.1, node.0.clone());
} ifself.doc_stack.is_empty() { self.doc_stack.push(node);
} else { let parent = self.doc_stack.last_mut().unwrap(); match *parent {
(Yaml::Array(refmut v), _) => v.push(node.0),
(Yaml::Hash(refmut h), _) => { let cur_key = self.key_stack.last_mut().unwrap(); // current node is a key if cur_key.is_badvalue() {
*cur_key = node.0; // current node is a value
} else { letmut newkey = Yaml::BadValue;
mem::swap(&mut newkey, cur_key); if h.insert(newkey, node.0).is_some() { let inserted_key = h.back().unwrap().0; return Err(ScanError::new_string(
mark,
format!("{inserted_key:?}: duplicated key in mapping"),
));
}
}
}
_ => unreachable!(),
}
}
Ok(())
}
/// Load the given string as a set of YAML documents. /// /// The `source` is interpreted as YAML documents and is parsed. Parsing succeeds if and only /// if all documents are parsed successfully. An error in a latter document prevents the former /// from being returned. /// # Errors /// Returns `ScanError` when loading fails. pubfn load_from_str(source: &str) -> Result<Vec<Yaml>, ScanError> { Self::load_from_iter(source.chars())
}
/// Load the contents of the given iterator as a set of YAML documents. /// /// The `source` is interpreted as YAML documents and is parsed. Parsing succeeds if and only /// if all documents are parsed successfully. An error in a latter document prevents the former /// from being returned. /// # Errors /// Returns `ScanError` when loading fails. pubfn load_from_iter<I: Iterator<Item = char>>(source: I) -> Result<Vec<Yaml>, ScanError> { letmut parser = Parser::new(source); Self::load_from_parser(&mut parser)
}
/// Load the contents from the specified Parser as a set of YAML documents. /// /// Parsing succeeds if and only if all documents are parsed successfully. /// An error in a latter document prevents the former from being returned. /// # Errors /// Returns `ScanError` when loading fails. pubfn load_from_parser<I: Iterator<Item = char>>(
parser: &mut Parser<I>,
) -> Result<Vec<Yaml>, ScanError> { letmut loader = YamlLoader::default();
parser.load(&mut loader, true)?; iflet Some(e) = loader.error {
Err(e)
} else {
Ok(loader.docs)
}
}
/// Return a reference to the parsed Yaml documents. #[must_use] pubfn documents(&self) -> &[Yaml] {
&self.docs
}
}
/// The signature of the function to call when using [`YAMLDecodingTrap::Call`]. /// /// The arguments are as follows: /// * `malformation_length`: The length of the sequence the decoder failed to decode. /// * `bytes_read_after_malformation`: The number of lookahead bytes the decoder consumed after /// the malformation. /// * `input_at_malformation`: What the input buffer is at the malformation. /// This is the buffer starting at the malformation. The first `malformation_length` bytes are /// the problematic sequence. The following `bytes_read_after_malformation` are already stored /// in the decoder and will not be re-fed. /// * `output`: The output string. /// /// The function must modify `output` as it feels is best. For instance, one could recreate the /// behavior of [`YAMLDecodingTrap::Ignore`] with an empty function, [`YAMLDecodingTrap::Replace`] /// by pushing a `\u{FFFD}` into `output` and [`YAMLDecodingTrap::Strict`] by returning /// [`ControlFlow::Break`]. /// /// # Returns /// The function must return [`ControlFlow::Continue`] if decoding may continue or /// [`ControlFlow::Break`] if decoding must be aborted. An optional error string may be supplied. #[cfg(feature = "encoding")] pubtype YAMLDecodingTrapFn = fn(
malformation_length: u8,
bytes_read_after_malformation: u8,
input_at_malformation: &[u8],
output: &mut String,
) -> ControlFlow<Cow<'static, str>>;
/// The behavior [`YamlDecoder`] must have when an decoding error occurs. #[cfg(feature = "encoding")] #[derive(Copy, Clone)] pubenum YAMLDecodingTrap { /// Ignore the offending bytes, remove them from the output.
Ignore, /// Error out.
Strict, /// Replace them with the Unicode REPLACEMENT CHARACTER.
Replace, /// Call the user-supplied function upon decoding malformation.
Call(YAMLDecodingTrapFn),
}
impl PartialEq for YAMLDecodingTrap { fn eq(&self, other: &YAMLDecodingTrap) -> bool { match (self, other) {
(YAMLDecodingTrap::Call(self_fn), YAMLDecodingTrap::Call(other_fn)) => {
*self_fn as usize == *other_fn as usize
}
(x, y) => x == y,
}
}
}
impl Eq for YAMLDecodingTrap {}
/// `YamlDecoder` is a `YamlLoader` builder that allows you to supply your own encoding error trap. /// For example, to read a YAML file while ignoring Unicode decoding errors you can set the /// `encoding_trap` to `encoding::DecoderTrap::Ignore`. /// ```rust /// use yaml_rust2::yaml::{YamlDecoder, YAMLDecodingTrap}; /// /// let string = b"--- /// a\xa9: 1 /// b: 2.2 /// c: [1, 2] /// "; /// let out = YamlDecoder::read(string as &[u8]) /// .encoding_trap(YAMLDecodingTrap::Ignore) /// .decode() /// .unwrap(); /// ``` #[cfg(feature = "encoding")] pubstruct YamlDecoder<T: std::io::Read> {
source: T,
trap: YAMLDecodingTrap,
}
#[cfg(feature = "encoding")] impl<T: std::io::Read> YamlDecoder<T> { /// Create a `YamlDecoder` decoding the given source. pubfn read(source: T) -> YamlDecoder<T> {
YamlDecoder {
source,
trap: YAMLDecodingTrap::Strict,
}
}
/// Set the behavior of the decoder when the encoding is invalid. pubfn encoding_trap(&mutself, trap: YAMLDecodingTrap) -> &yle='color:red'>mutSelf { self.trap = trap; self
}
/// Run the decode operation with the source and trap the `YamlDecoder` was built with. /// /// # Errors /// Returns `LoadError` when decoding fails. pubfn decode(&mutself) -> Result<Vec<Yaml>, LoadError> { letmut buffer = Vec::new(); self.source.read_to_end(&mut buffer)?;
// Check if the `encoding` library can detect encoding from the BOM, otherwise use // `detect_utf16_endianness`. let (encoding, _) =
Encoding::for_bom(&buffer).unwrap_or_else(|| (detect_utf16_endianness(&buffer), 2)); letmut decoder = encoding.new_decoder(); letmut output = String::new();
loop { match decoder.decode_to_string_without_replacement(&input[total_bytes_read..], output, true)
{ // If the input is empty, we processed the whole input.
(DecoderResult::InputEmpty, _) => break Ok(()), // If the output is full, we must reallocate.
(DecoderResult::OutputFull, bytes_read) => {
total_bytes_read += bytes_read; // The output is already reserved to the size of the input. We slowly resize. Here, // we're expecting that 10% of bytes will double in size when converting to UTF-8.
output.reserve(input.len() / 10);
}
(DecoderResult::Malformed(malformed_len, bytes_after_malformed), bytes_read) => {
total_bytes_read += bytes_read; match trap { // Ignore (skip over) malformed character.
YAMLDecodingTrap::Ignore => {} // Replace them with the Unicode REPLACEMENT CHARACTER.
YAMLDecodingTrap::Replace => {
output.push('\u{FFFD}');
} // Otherwise error, getting as much context as possible.
YAMLDecodingTrap::Strict => { let malformed_len = malformed_len as usize; let bytes_after_malformed = bytes_after_malformed as usize; let byte_idx = total_bytes_read - (malformed_len + bytes_after_malformed); let malformed_sequence = &input[byte_idx..byte_idx + malformed_len];
break Err(LoadError::Decode(Cow::Owned(format!( "Invalid character sequence at {byte_idx}: {malformed_sequence:?}",
))));
}
YAMLDecodingTrap::Call(callback) => { let byte_idx =
total_bytes_read - ((malformed_len + bytes_after_malformed) as usize); let malformed_sequence =
&input[byte_idx..byte_idx + malformed_len as usize]; iflet ControlFlow::Break(error) = callback(
malformed_len,
bytes_after_malformed,
&input[byte_idx..],
output,
) { if error.is_empty() { break Err(LoadError::Decode(Cow::Owned(format!( "Invalid character sequence at {byte_idx}: {malformed_sequence:?}",
))));
} break Err(LoadError::Decode(error));
}
}
}
}
}
}
}
/// The encoding crate knows how to tell apart UTF-8 from UTF-16LE and utf-16BE, when the /// bytestream starts with BOM codepoint. /// However, it doesn't even attempt to guess the UTF-16 endianness of the input bytestream since /// in the general case the bytestream could start with a codepoint that uses both bytes. /// /// The YAML-1.2 spec mandates that the first character of a YAML document is an ASCII character. /// This allows the encoding to be deduced by the pattern of null (#x00) characters. // /// See spec at <https://yaml.org/spec/1.2/spec.html#id2771184> #[cfg(feature = "encoding")] fn detect_utf16_endianness(b: &[u8]) -> &'static Encoding { if b.len() > 1 && (b[0] != b[1]) { if b[0] == 0 { return encoding_rs::UTF_16BE;
} elseif b[1] == 0 { return encoding_rs::UTF_16LE;
}
}
encoding_rs::UTF_8
}
macro_rules! define_as (
($name:ident, $t:ident, $yt:ident) => ( /// Get a copy of the inner object in the YAML enum if it is a `$t`. /// /// # Return /// If the variant of `self` is `Yaml::$yt`, return `Some($t)` with a copy of the `$t` contained. /// Otherwise, return `None`. #[must_use] pubfn $name(&self) -> Option<$t> { match *self {
Yaml::$yt(v) => Some(v),
_ => None
}
}
);
);
macro_rules! define_as_ref (
($name:ident, $t:ty, $yt:ident) => ( /// Get a reference to the inner object in the YAML enum if it is a `$t`. /// /// # Return /// If the variant of `self` is `Yaml::$yt`, return `Some(&$t)` with the `$t` contained. Otherwise, /// return `None`. #[must_use] pubfn $name(&self) -> Option<$t> { match *self {
Yaml::$yt(ref v) => Some(v),
_ => None
}
}
);
);
macro_rules! define_as_mut_ref (
($name:ident, $t:ty, $yt:ident) => ( /// Get a mutable reference to the inner object in the YAML enum if it is a `$t`. /// /// # Return /// If the variant of `self` is `Yaml::$yt`, return `Some(&mut $t)` with the `$t` contained. /// Otherwise, return `None`. #[must_use] pubfn $name(&mutself) -> Option<$t> { match *self {
Yaml::$yt(refmut v) => Some(v),
_ => None
}
}
);
);
macro_rules! define_into (
($name:ident, $t:ty, $yt:ident) => ( /// Get the inner object in the YAML enum if it is a `$t`. /// /// # Return /// If the variant of `self` is `Yaml::$yt`, return `Some($t)` with the `$t` contained. Otherwise, /// return `None`. #[must_use] pubfn $name(self) -> Option<$t> { matchself {
Yaml::$yt(v) => Some(v),
_ => None
}
}
);
);
/// Return whether `self` is a [`Yaml::Null`] node. #[must_use] pubfn is_null(&self) -> bool {
matches!(*self, Yaml::Null)
}
/// Return whether `self` is a [`Yaml::BadValue`] node. #[must_use] pubfn is_badvalue(&self) -> bool {
matches!(*self, Yaml::BadValue)
}
/// Return whether `self` is a [`Yaml::Array`] node. #[must_use] pubfn is_array(&self) -> bool {
matches!(*self, Yaml::Array(_))
}
/// Return whether `self` is a [`Yaml::Hash`] node. #[must_use] pubfn is_hash(&self) -> bool {
matches!(*self, Yaml::Hash(_))
}
/// Return the `f64` value contained in this YAML node. /// /// If the node is not a [`Yaml::Real`] YAML node or its contents is not a valid `f64` string, /// `None` is returned. #[must_use] pubfn as_f64(&self) -> Option<f64> { iflet Yaml::Real(ref v) = self {
parse_f64(v)
} else {
None
}
}
/// Return the `f64` value contained in this YAML node. /// /// If the node is not a [`Yaml::Real`] YAML node or its contents is not a valid `f64` string, /// `None` is returned. #[must_use] pubfn into_f64(self) -> Option<f64> { self.as_f64()
}
/// If a value is null or otherwise bad (see variants), consume it and /// replace it with a given value `other`. Otherwise, return self unchanged. /// /// ``` /// use yaml_rust2::yaml::Yaml; /// /// assert_eq!(Yaml::BadValue.or(Yaml::Integer(3)), Yaml::Integer(3)); /// assert_eq!(Yaml::Integer(3).or(Yaml::BadValue), Yaml::Integer(3)); /// ``` #[must_use] pubfn or(self, other: Self) -> Self { matchself {
Yaml::BadValue | Yaml::Null => other,
this => this,
}
}
/// See `or` for behavior. This performs the same operations, but with /// borrowed values for less linear pipelines. #[must_use] pubfn borrowed_or<'a>(&'a self, other: &'a Self) -> &'a Self{ matchself {
Yaml::BadValue | Yaml::Null => other,
this => this,
}
}
}
static BAD_VALUE: Yaml = Yaml::BadValue; impl<'a> Index<&'a str> for Yaml { type Output = Yaml;
/// Perform indexing if `self` is a mapping. /// /// # Return /// If `self` is a [`Yaml::Hash`], returns an immutable borrow to the value associated to the /// given key in the hash. /// /// This function returns a [`Yaml::BadValue`] if the underlying [`type@Hash`] does not contain /// [`Yaml::String`]`{idx}` as a key. /// /// This function also returns a [`Yaml::BadValue`] if `self` is not a [`Yaml::Hash`]. fn index(&self, idx: &'a str) -> &Yaml { let key = Yaml::String(idx.to_owned()); matchself.as_hash() {
Some(h) => h.get(&key).unwrap_or(&BAD_VALUE),
None => &BAD_VALUE,
}
}
}
impl<'a> IndexMut<&'a str> for Yaml { /// Perform indexing if `self` is a mapping. /// /// Since we cannot return a mutable borrow to a static [`Yaml::BadValue`] as we return an /// immutable one in [`Index<&'a str>`], this function panics on out of bounds. /// /// # Panics /// This function panics if the given key is not contained in `self` (as per [`IndexMut`]). /// /// This function also panics if `self` is not a [`Yaml::Hash`]. fn index_mut(&mutself, idx: &'a str) -> &mut Yaml { let key = Yaml::String(idx.to_owned()); matchself.as_mut_hash() {
Some(h) => h.get_mut(&key).unwrap(),
None => panic!("Not a hash type"),
}
}
}
impl Index<usize> for Yaml { type Output = Yaml;
/// Perform indexing if `self` is a sequence or a mapping. /// /// # Return /// If `self` is a [`Yaml::Array`], returns an immutable borrow to the value located at the /// given index in the array. /// /// Otherwise, if `self` is a [`Yaml::Hash`], returns a borrow to the value whose key is /// [`Yaml::Integer`]`(idx)` (this would not work if the key is [`Yaml::String`]`("1")`. /// /// This function returns a [`Yaml::BadValue`] if the index given is out of range. If `self` is /// a [`Yaml::Array`], this is when the index is bigger or equal to the length of the /// underlying `Vec`. If `self` is a [`Yaml::Hash`], this is when the mapping sequence does not /// contain [`Yaml::Integer`]`(idx)` as a key. /// /// This function also returns a [`Yaml::BadValue`] if `self` is not a [`Yaml::Array`] nor a /// [`Yaml::Hash`]. fn index(&self, idx: usize) -> &Yaml { iflet Some(v) = self.as_vec() {
v.get(idx).unwrap_or(&BAD_VALUE)
} elseiflet Some(v) = self.as_hash() { let key = Yaml::Integer(i64::try_from(idx).unwrap());
v.get(&key).unwrap_or(&BAD_VALUE)
} else {
&BAD_VALUE
}
}
}
impl IndexMut<usize> for Yaml { /// Perform indexing if `self` is a sequence or a mapping. /// /// Since we cannot return a mutable borrow to a static [`Yaml::BadValue`] as we return an /// immutable one in [`Index<usize>`], this function panics on out of bounds. /// /// # Panics /// This function panics if the index given is out of range (as per [`IndexMut`]). If `self` is /// a [`Yaml::Array`], this is when the index is bigger or equal to the length of the /// underlying `Vec`. If `self` is a [`Yaml::Hash`], this is when the mapping sequence does not /// contain [`Yaml::Integer`]`(idx)` as a key. /// /// This function also panics if `self` is not a [`Yaml::Array`] nor a [`Yaml::Hash`]. fn index_mut(&mutself, idx: usize) -> &mutYaml { matchself {
Yaml::Array(sequence) => sequence.index_mut(idx),
Yaml::Hash(mapping) => { let key = Yaml::Integer(i64::try_from(idx).unwrap());
mapping.get_mut(&key).unwrap()
}
_ => panic!("Attempting to index but `self` is not a sequence nor a mapping"),
}
}
}
impl IntoIterator for Yaml { type Item = Yaml; type IntoIter = YamlIter;
/// Extract the [`Array`] from `self` and iterate over it. /// /// If `self` is **not** of the [`Yaml::Array`] variant, this function will not panic or return /// an error (as per the [`IntoIterator`] trait it cannot) but will instead return an iterator /// over an empty [`Array`]. Callers have to ensure (using [`Yaml::is_array`], [`matches`] or /// something similar) that the [`Yaml`] object is a [`Yaml::Array`] if they want to do error /// handling. /// /// # Examples /// ``` /// # use yaml_rust2::{Yaml, YamlLoader}; /// /// // An array of 2 integers, 1 and 2. /// let arr = &YamlLoader::load_from_str("- 1\n- 2").unwrap()[0]; /// /// assert_eq!(arr.clone().into_iter().count(), 2); /// assert_eq!(arr.clone().into_iter().next(), Some(Yaml::Integer(1))); /// assert_eq!(arr.clone().into_iter().nth(1), Some(Yaml::Integer(2))); /// /// // An empty array returns an empty iterator. /// let empty = Yaml::Array(vec![]); /// assert_eq!(empty.into_iter().count(), 0); /// /// // A hash with 2 key-value pairs, `(a, b)` and `(c, d)`. /// let hash = YamlLoader::load_from_str("a: b\nc: d").unwrap().remove(0); /// // The hash has 2 elements. /// assert_eq!(hash.as_hash().unwrap().iter().count(), 2); /// // But since `into_iter` can't be used with a `Yaml::Hash`, `into_iter` returns an empty /// // iterator. /// assert_eq!(hash.into_iter().count(), 0); /// ``` fn into_iter(self) -> Self::IntoIter {
YamlIter {
yaml: self.into_vec().unwrap_or_default().into_iter(),
}
}
}
/// An iterator over a [`Yaml`] node. pubstruct YamlIter {
yaml: std::vec::IntoIter<Yaml>,
}
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.