2023-08-11 23:54:46 +00:00
|
|
|
#![allow(clippy::module_name_repetitions)]
|
|
|
|
|
2024-02-08 06:12:14 +00:00
|
|
|
use std::{collections::BTreeMap, convert::TryFrom, mem, ops::Index};
|
|
|
|
|
2020-05-26 10:35:06 +00:00
|
|
|
use hashlink::LinkedHashMap;
|
2024-02-08 06:12:14 +00:00
|
|
|
|
2023-11-19 00:09:41 +00:00
|
|
|
use crate::parser::{Event, MarkedEventReceiver, Parser, Tag};
|
2023-12-20 23:14:08 +00:00
|
|
|
use crate::scanner::{Marker, ScanError, TScalarStyle};
|
2015-05-24 06:27:42 +00:00
|
|
|
|
2016-03-10 09:55:21 +00:00
|
|
|
/// A YAML node is stored as this `Yaml` enumeration, which provides an easy way to
|
2015-05-31 09:59:43 +00:00
|
|
|
/// access your YAML document.
|
2015-06-29 16:31:22 +00:00
|
|
|
///
|
2015-05-31 09:59:43 +00:00
|
|
|
/// # Examples
|
2015-06-29 16:31:22 +00:00
|
|
|
///
|
2015-05-31 09:59:43 +00:00
|
|
|
/// ```
|
2024-02-08 06:12:14 +00:00
|
|
|
/// use yaml_rust2::Yaml;
|
2015-05-31 09:59:43 +00:00
|
|
|
/// let foo = Yaml::from_str("-123"); // convert the string to the appropriate YAML type
|
|
|
|
/// assert_eq!(foo.as_i64().unwrap(), -123);
|
2015-06-29 16:31:22 +00:00
|
|
|
///
|
2016-03-10 09:55:21 +00:00
|
|
|
/// // iterate over an Array
|
2015-05-31 09:59:43 +00:00
|
|
|
/// let vec = Yaml::Array(vec![Yaml::Integer(1), Yaml::Integer(2)]);
|
|
|
|
/// for v in vec.as_vec().unwrap() {
|
|
|
|
/// assert!(v.as_i64().is_some());
|
|
|
|
/// }
|
|
|
|
/// ```
|
2016-03-20 05:06:44 +00:00
|
|
|
#[derive(Clone, PartialEq, PartialOrd, Debug, Eq, Ord, Hash)]
|
2015-05-24 06:27:42 +00:00
|
|
|
pub enum Yaml {
|
2016-03-10 09:55:21 +00:00
|
|
|
/// Float types are stored as String and parsed on demand.
|
2023-08-17 00:17:40 +00:00
|
|
|
/// Note that `f64` does NOT implement Eq trait and can NOT be stored in `BTreeMap`.
|
|
|
|
Real(String),
|
2016-03-10 09:55:21 +00:00
|
|
|
/// YAML int is stored as i64.
|
2015-05-30 14:39:50 +00:00
|
|
|
Integer(i64),
|
2016-03-10 09:55:21 +00:00
|
|
|
/// YAML scalar.
|
2023-08-17 00:17:40 +00:00
|
|
|
String(String),
|
2016-03-10 09:55:21 +00:00
|
|
|
/// YAML bool, e.g. `true` or `false`.
|
2015-05-24 06:27:42 +00:00
|
|
|
Boolean(bool),
|
2016-03-10 09:55:21 +00:00
|
|
|
/// YAML array, can be accessed as a `Vec`.
|
2023-08-17 00:17:40 +00:00
|
|
|
Array(Array),
|
2017-01-28 04:50:52 +00:00
|
|
|
/// YAML hash, can be accessed as a `LinkedHashMap`.
|
2016-03-20 05:06:44 +00:00
|
|
|
///
|
2019-02-20 07:23:31 +00:00
|
|
|
/// Insertion order will match the order of insertion into the map.
|
2023-08-17 00:17:40 +00:00
|
|
|
Hash(Hash),
|
2015-05-31 09:59:43 +00:00
|
|
|
/// Alias, not fully supported yet.
|
2015-05-28 17:56:03 +00:00
|
|
|
Alias(usize),
|
2016-03-10 09:55:21 +00:00
|
|
|
/// YAML null, e.g. `null` or `~`.
|
2015-05-24 06:27:42 +00:00
|
|
|
Null,
|
2016-03-10 09:55:21 +00:00
|
|
|
/// Accessing a nonexistent node via the Index trait returns `BadValue`. This
|
|
|
|
/// simplifies error handling in the calling code. Invalid type conversion also
|
|
|
|
/// returns `BadValue`.
|
2015-05-24 18:16:28 +00:00
|
|
|
BadValue,
|
2015-05-24 06:27:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub type Array = Vec<Yaml>;
|
2017-01-28 04:50:52 +00:00
|
|
|
pub type Hash = LinkedHashMap<Yaml, Yaml>;
|
2015-05-24 06:27:42 +00:00
|
|
|
|
2017-05-13 13:22:19 +00:00
|
|
|
// 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),
|
2018-09-15 16:49:04 +00:00
|
|
|
_ => v.parse::<f64>().ok(),
|
2017-05-13 13:22:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-17 00:17:40 +00:00
|
|
|
/// Main structure for quickly parsing YAML.
|
|
|
|
///
|
|
|
|
/// See [`YamlLoader::load_from_str`].
|
2020-05-05 12:53:06 +00:00
|
|
|
#[derive(Default)]
|
2015-05-26 18:50:51 +00:00
|
|
|
pub struct YamlLoader {
|
|
|
|
docs: Vec<Yaml>,
|
|
|
|
// states
|
2015-12-16 07:10:02 +00:00
|
|
|
// (current node, anchor_id) tuple
|
|
|
|
doc_stack: Vec<(Yaml, usize)>,
|
2015-05-26 18:50:51 +00:00
|
|
|
key_stack: Vec<Yaml>,
|
2015-12-16 07:10:02 +00:00
|
|
|
anchor_map: BTreeMap<usize, Yaml>,
|
2015-05-26 18:50:51 +00:00
|
|
|
}
|
|
|
|
|
2017-06-19 13:41:26 +00:00
|
|
|
impl MarkedEventReceiver for YamlLoader {
|
|
|
|
fn on_event(&mut self, ev: Event, _: Marker) {
|
2015-05-30 14:39:50 +00:00
|
|
|
// println!("EV {:?}", ev);
|
2017-06-14 08:29:27 +00:00
|
|
|
match ev {
|
2015-05-26 18:50:51 +00:00
|
|
|
Event::DocumentStart => {
|
|
|
|
// do nothing
|
2018-09-15 16:49:04 +00:00
|
|
|
}
|
2015-05-26 18:50:51 +00:00
|
|
|
Event::DocumentEnd => {
|
|
|
|
match self.doc_stack.len() {
|
|
|
|
// empty document
|
|
|
|
0 => self.docs.push(Yaml::BadValue),
|
2015-12-16 07:10:02 +00:00
|
|
|
1 => self.docs.push(self.doc_stack.pop().unwrap().0),
|
2018-09-15 16:49:04 +00:00
|
|
|
_ => unreachable!(),
|
2015-05-26 18:50:51 +00:00
|
|
|
}
|
2018-09-15 16:49:04 +00:00
|
|
|
}
|
2023-11-19 13:40:01 +00:00
|
|
|
Event::SequenceStart(aid, _) => {
|
2015-12-16 07:10:02 +00:00
|
|
|
self.doc_stack.push((Yaml::Array(Vec::new()), aid));
|
2018-09-15 16:49:04 +00:00
|
|
|
}
|
2015-05-26 18:50:51 +00:00
|
|
|
Event::SequenceEnd => {
|
|
|
|
let node = self.doc_stack.pop().unwrap();
|
|
|
|
self.insert_new_node(node);
|
2018-09-15 16:49:04 +00:00
|
|
|
}
|
2023-11-19 00:09:41 +00:00
|
|
|
Event::MappingStart(aid, _) => {
|
2015-12-16 07:10:02 +00:00
|
|
|
self.doc_stack.push((Yaml::Hash(Hash::new()), aid));
|
2015-05-26 18:50:51 +00:00
|
|
|
self.key_stack.push(Yaml::BadValue);
|
2018-09-15 16:49:04 +00:00
|
|
|
}
|
2015-05-26 18:50:51 +00:00
|
|
|
Event::MappingEnd => {
|
|
|
|
self.key_stack.pop().unwrap();
|
|
|
|
let node = self.doc_stack.pop().unwrap();
|
|
|
|
self.insert_new_node(node);
|
2018-09-15 16:49:04 +00:00
|
|
|
}
|
2017-06-14 08:29:27 +00:00
|
|
|
Event::Scalar(v, style, aid, tag) => {
|
2015-05-26 18:50:51 +00:00
|
|
|
let node = if style != TScalarStyle::Plain {
|
2017-06-14 08:29:27 +00:00
|
|
|
Yaml::String(v)
|
2023-11-19 13:40:01 +00:00
|
|
|
} else if let Some(Tag {
|
|
|
|
ref handle,
|
|
|
|
ref suffix,
|
|
|
|
}) = tag
|
|
|
|
{
|
2023-11-19 15:00:19 +00:00
|
|
|
if handle == "tag:yaml.org,2002:" {
|
2016-02-28 00:30:13 +00:00
|
|
|
match suffix.as_ref() {
|
|
|
|
"bool" => {
|
|
|
|
// "true" or "false"
|
|
|
|
match v.parse::<bool>() {
|
|
|
|
Err(_) => Yaml::BadValue,
|
2018-09-15 16:49:04 +00:00
|
|
|
Ok(v) => Yaml::Boolean(v),
|
2016-02-28 00:30:13 +00:00
|
|
|
}
|
2018-09-15 16:49:04 +00:00
|
|
|
}
|
|
|
|
"int" => match v.parse::<i64>() {
|
|
|
|
Err(_) => Yaml::BadValue,
|
|
|
|
Ok(v) => Yaml::Integer(v),
|
2016-02-28 00:30:13 +00:00
|
|
|
},
|
2018-09-15 16:49:04 +00:00
|
|
|
"float" => match parse_f64(&v) {
|
|
|
|
Some(_) => Yaml::Real(v),
|
|
|
|
None => Yaml::BadValue,
|
2016-02-28 00:30:13 +00:00
|
|
|
},
|
2018-09-15 16:49:04 +00:00
|
|
|
"null" => match v.as_ref() {
|
|
|
|
"~" | "null" => Yaml::Null,
|
|
|
|
_ => Yaml::BadValue,
|
2016-02-28 00:30:13 +00:00
|
|
|
},
|
2018-09-15 16:49:04 +00:00
|
|
|
_ => Yaml::String(v),
|
2016-02-28 00:30:13 +00:00
|
|
|
}
|
|
|
|
} else {
|
2017-06-14 08:29:27 +00:00
|
|
|
Yaml::String(v)
|
2015-05-26 18:50:51 +00:00
|
|
|
}
|
2016-02-28 00:30:13 +00:00
|
|
|
} else {
|
|
|
|
// Datatype is not specified, or unrecognized
|
2017-06-14 08:29:27 +00:00
|
|
|
Yaml::from_str(&v)
|
2015-05-26 18:50:51 +00:00
|
|
|
};
|
|
|
|
|
2015-12-16 07:10:02 +00:00
|
|
|
self.insert_new_node((node, aid));
|
2018-09-15 16:49:04 +00:00
|
|
|
}
|
2015-05-28 17:56:03 +00:00
|
|
|
Event::Alias(id) => {
|
2015-12-16 07:10:02 +00:00
|
|
|
let n = match self.anchor_map.get(&id) {
|
|
|
|
Some(v) => v.clone(),
|
|
|
|
None => Yaml::BadValue,
|
|
|
|
};
|
|
|
|
self.insert_new_node((n, 0));
|
2015-05-28 17:56:03 +00:00
|
|
|
}
|
2015-05-26 18:50:51 +00:00
|
|
|
_ => { /* ignore */ }
|
|
|
|
}
|
|
|
|
// println!("DOC {:?}", self.doc_stack);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-05 12:53:06 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum LoadError {
|
|
|
|
IO(std::io::Error),
|
|
|
|
Scan(ScanError),
|
|
|
|
Decode(std::borrow::Cow<'static, str>),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<std::io::Error> for LoadError {
|
|
|
|
fn from(error: std::io::Error) -> Self {
|
|
|
|
LoadError::IO(error)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-26 18:50:51 +00:00
|
|
|
impl YamlLoader {
|
2015-12-16 07:10:02 +00:00
|
|
|
fn insert_new_node(&mut self, node: (Yaml, usize)) {
|
|
|
|
// valid anchor id starts from 1
|
|
|
|
if node.1 > 0 {
|
|
|
|
self.anchor_map.insert(node.1, node.0.clone());
|
|
|
|
}
|
2016-03-10 12:49:02 +00:00
|
|
|
if self.doc_stack.is_empty() {
|
|
|
|
self.doc_stack.push(node);
|
|
|
|
} else {
|
2015-05-26 18:50:51 +00:00
|
|
|
let parent = self.doc_stack.last_mut().unwrap();
|
|
|
|
match *parent {
|
2015-12-16 07:10:02 +00:00
|
|
|
(Yaml::Array(ref mut v), _) => v.push(node.0),
|
|
|
|
(Yaml::Hash(ref mut h), _) => {
|
2018-01-02 06:57:27 +00:00
|
|
|
let cur_key = self.key_stack.last_mut().unwrap();
|
2015-05-26 18:50:51 +00:00
|
|
|
// current node is a key
|
|
|
|
if cur_key.is_badvalue() {
|
2015-12-16 07:10:02 +00:00
|
|
|
*cur_key = node.0;
|
2015-05-26 18:50:51 +00:00
|
|
|
// current node is a value
|
|
|
|
} else {
|
|
|
|
let mut newkey = Yaml::BadValue;
|
|
|
|
mem::swap(&mut newkey, cur_key);
|
2015-12-16 07:10:02 +00:00
|
|
|
h.insert(newkey, node.0);
|
2015-05-26 18:50:51 +00:00
|
|
|
}
|
2018-09-15 16:49:04 +00:00
|
|
|
}
|
2015-05-26 18:50:51 +00:00
|
|
|
_ => unreachable!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-17 00:17:40 +00:00
|
|
|
/// 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.
|
2018-09-15 16:49:04 +00:00
|
|
|
pub fn load_from_str(source: &str) -> Result<Vec<Yaml>, ScanError> {
|
2023-08-17 00:17:40 +00:00
|
|
|
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.
|
|
|
|
pub fn load_from_iter<I: Iterator<Item = char>>(source: I) -> Result<Vec<Yaml>, ScanError> {
|
2020-05-05 12:53:06 +00:00
|
|
|
let mut loader = YamlLoader::default();
|
2023-08-17 00:17:40 +00:00
|
|
|
let mut parser = Parser::new(source);
|
2018-09-16 06:58:48 +00:00
|
|
|
parser.load(&mut loader, true)?;
|
2015-05-26 18:50:51 +00:00
|
|
|
Ok(loader.docs)
|
|
|
|
}
|
2020-05-05 12:53:06 +00:00
|
|
|
|
|
|
|
pub fn load_from_bytes(mut source: impl std::io::Read) -> Result<Vec<Yaml>, LoadError> {
|
|
|
|
let mut buffer = Vec::new();
|
|
|
|
source.read_to_end(&mut buffer)?;
|
|
|
|
|
|
|
|
// Decodes the input buffer using either UTF-8, UTF-16LE or UTF-16BE depending on the BOM codepoint.
|
|
|
|
// If the buffer doesn't start with a BOM codepoint, it will use a fallback encoding obtained by
|
|
|
|
// detect_utf16_endianness.
|
|
|
|
let (res, _) = encoding::types::decode(
|
|
|
|
&buffer,
|
|
|
|
encoding::DecoderTrap::Strict,
|
|
|
|
detect_utf16_endianness(&buffer),
|
|
|
|
);
|
|
|
|
let s = res.map_err(LoadError::Decode)?;
|
|
|
|
YamlLoader::load_from_str(&s).map_err(LoadError::Scan)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 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>
|
|
|
|
fn detect_utf16_endianness(b: &[u8]) -> encoding::types::EncodingRef {
|
|
|
|
if b.len() > 1 && (b[0] != b[1]) {
|
|
|
|
if b[0] == 0 {
|
|
|
|
return encoding::all::UTF_16BE;
|
|
|
|
} else if b[1] == 0 {
|
|
|
|
return encoding::all::UTF_16LE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
encoding::all::UTF_8
|
2015-05-26 18:50:51 +00:00
|
|
|
}
|
|
|
|
|
2015-05-24 17:34:18 +00:00
|
|
|
macro_rules! define_as (
|
|
|
|
($name:ident, $t:ident, $yt:ident) => (
|
2023-08-11 23:54:46 +00:00
|
|
|
#[must_use]
|
2015-05-24 17:34:18 +00:00
|
|
|
pub fn $name(&self) -> Option<$t> {
|
|
|
|
match *self {
|
|
|
|
Yaml::$yt(v) => Some(v),
|
|
|
|
_ => None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
macro_rules! define_as_ref (
|
|
|
|
($name:ident, $t:ty, $yt:ident) => (
|
2023-08-11 23:54:46 +00:00
|
|
|
#[must_use]
|
2015-05-24 17:34:18 +00:00
|
|
|
pub fn $name(&self) -> Option<$t> {
|
|
|
|
match *self {
|
|
|
|
Yaml::$yt(ref v) => Some(v),
|
|
|
|
_ => None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
2016-08-08 02:25:30 +00:00
|
|
|
macro_rules! define_into (
|
|
|
|
($name:ident, $t:ty, $yt:ident) => (
|
2023-08-11 23:54:46 +00:00
|
|
|
#[must_use]
|
2016-08-08 02:25:30 +00:00
|
|
|
pub fn $name(self) -> Option<$t> {
|
|
|
|
match self {
|
|
|
|
Yaml::$yt(v) => Some(v),
|
|
|
|
_ => None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
2016-08-08 21:52:24 +00:00
|
|
|
);
|
2016-08-08 02:25:30 +00:00
|
|
|
|
2015-05-24 17:34:18 +00:00
|
|
|
impl Yaml {
|
|
|
|
define_as!(as_bool, bool, Boolean);
|
2015-05-30 14:39:50 +00:00
|
|
|
define_as!(as_i64, i64, Integer);
|
2015-05-24 17:34:18 +00:00
|
|
|
|
|
|
|
define_as_ref!(as_str, &str, String);
|
|
|
|
define_as_ref!(as_hash, &Hash, Hash);
|
|
|
|
define_as_ref!(as_vec, &Array, Array);
|
|
|
|
|
2016-08-08 02:25:30 +00:00
|
|
|
define_into!(into_bool, bool, Boolean);
|
2016-08-08 21:52:24 +00:00
|
|
|
define_into!(into_i64, i64, Integer);
|
2016-08-08 02:25:30 +00:00
|
|
|
define_into!(into_string, String, String);
|
|
|
|
define_into!(into_hash, Hash, Hash);
|
|
|
|
define_into!(into_vec, Array, Array);
|
|
|
|
|
2023-08-17 00:17:40 +00:00
|
|
|
/// Return whether `self` is a [`Yaml::Null`] node.
|
2023-08-11 23:54:46 +00:00
|
|
|
#[must_use]
|
2015-05-24 17:34:18 +00:00
|
|
|
pub fn is_null(&self) -> bool {
|
2023-08-11 23:54:46 +00:00
|
|
|
matches!(*self, Yaml::Null)
|
2015-05-24 17:34:18 +00:00
|
|
|
}
|
|
|
|
|
2023-08-17 00:17:40 +00:00
|
|
|
/// Return whether `self` is a [`Yaml::BadValue`] node.
|
2023-08-11 23:54:46 +00:00
|
|
|
#[must_use]
|
2015-05-24 18:16:28 +00:00
|
|
|
pub fn is_badvalue(&self) -> bool {
|
2023-08-11 23:54:46 +00:00
|
|
|
matches!(*self, Yaml::BadValue)
|
2015-05-24 18:16:28 +00:00
|
|
|
}
|
|
|
|
|
2023-08-17 00:17:40 +00:00
|
|
|
/// Return whether `self` is a [`Yaml::Array`] node.
|
2023-08-11 23:54:46 +00:00
|
|
|
#[must_use]
|
2017-06-10 21:39:07 +00:00
|
|
|
pub fn is_array(&self) -> bool {
|
2023-08-11 23:54:46 +00:00
|
|
|
matches!(*self, Yaml::Array(_))
|
2017-06-10 21:39:07 +00:00
|
|
|
}
|
|
|
|
|
2023-08-17 00:17:40 +00:00
|
|
|
/// 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.
|
2023-08-11 23:54:46 +00:00
|
|
|
#[must_use]
|
2015-05-30 14:39:50 +00:00
|
|
|
pub fn as_f64(&self) -> Option<f64> {
|
2023-08-11 23:54:46 +00:00
|
|
|
if let Yaml::Real(ref v) = self {
|
|
|
|
parse_f64(v)
|
|
|
|
} else {
|
|
|
|
None
|
2015-05-24 17:34:18 +00:00
|
|
|
}
|
|
|
|
}
|
2016-08-08 02:25:30 +00:00
|
|
|
|
2023-08-17 00:17:40 +00:00
|
|
|
/// 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.
|
2023-08-11 23:54:46 +00:00
|
|
|
#[must_use]
|
2016-08-08 02:25:30 +00:00
|
|
|
pub fn into_f64(self) -> Option<f64> {
|
2023-08-17 00:17:40 +00:00
|
|
|
self.as_f64()
|
2016-08-08 02:25:30 +00:00
|
|
|
}
|
2016-02-28 00:30:13 +00:00
|
|
|
}
|
2015-05-24 18:16:28 +00:00
|
|
|
|
2023-08-11 23:54:46 +00:00
|
|
|
#[cfg_attr(feature = "cargo-clippy", allow(clippy::should_implement_trait))]
|
2016-02-28 00:30:13 +00:00
|
|
|
impl Yaml {
|
2023-08-17 00:17:40 +00:00
|
|
|
/// Convert a string to a [`Yaml`] node.
|
|
|
|
///
|
|
|
|
/// [`Yaml`] does not implement [`std::str::FromStr`] since conversion may not fail. This
|
|
|
|
/// function falls back to [`Yaml::String`] if nothing else matches.
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
/// ```
|
2024-02-08 06:12:14 +00:00
|
|
|
/// # use yaml_rust2::yaml::Yaml;
|
2023-08-17 00:17:40 +00:00
|
|
|
/// assert!(matches!(Yaml::from_str("42"), Yaml::Integer(42)));
|
|
|
|
/// assert!(matches!(Yaml::from_str("0x2A"), Yaml::Integer(42)));
|
|
|
|
/// assert!(matches!(Yaml::from_str("0o52"), Yaml::Integer(42)));
|
|
|
|
/// assert!(matches!(Yaml::from_str("~"), Yaml::Null));
|
|
|
|
/// assert!(matches!(Yaml::from_str("null"), Yaml::Null));
|
|
|
|
/// assert!(matches!(Yaml::from_str("true"), Yaml::Boolean(true)));
|
|
|
|
/// assert!(matches!(Yaml::from_str("3.14"), Yaml::Real(_)));
|
|
|
|
/// assert!(matches!(Yaml::from_str("foo"), Yaml::String(_)));
|
|
|
|
/// ```
|
2023-08-11 23:54:46 +00:00
|
|
|
#[must_use]
|
2015-05-30 14:39:50 +00:00
|
|
|
pub fn from_str(v: &str) -> Yaml {
|
2023-08-11 23:54:46 +00:00
|
|
|
if let Some(number) = v.strip_prefix("0x") {
|
|
|
|
if let Ok(i) = i64::from_str_radix(number, 16) {
|
2020-06-01 12:34:13 +00:00
|
|
|
return Yaml::Integer(i);
|
2016-02-07 21:52:20 +00:00
|
|
|
}
|
2023-08-11 23:54:46 +00:00
|
|
|
} else if let Some(number) = v.strip_prefix("0o") {
|
|
|
|
if let Ok(i) = i64::from_str_radix(number, 8) {
|
2020-06-01 12:34:13 +00:00
|
|
|
return Yaml::Integer(i);
|
2016-02-07 21:52:20 +00:00
|
|
|
}
|
2023-08-11 23:54:46 +00:00
|
|
|
} else if let Some(number) = v.strip_prefix('+') {
|
|
|
|
if let Ok(i) = number.parse::<i64>() {
|
2020-06-01 12:34:13 +00:00
|
|
|
return Yaml::Integer(i);
|
|
|
|
}
|
2016-02-07 22:21:05 +00:00
|
|
|
}
|
2015-05-30 14:39:50 +00:00
|
|
|
match v {
|
|
|
|
"~" | "null" => Yaml::Null,
|
|
|
|
"true" => Yaml::Boolean(true),
|
|
|
|
"false" => Yaml::Boolean(false),
|
2023-08-11 23:54:46 +00:00
|
|
|
_ => {
|
|
|
|
if let Ok(integer) = v.parse::<i64>() {
|
|
|
|
Yaml::Integer(integer)
|
|
|
|
} else if parse_f64(v).is_some() {
|
|
|
|
Yaml::Real(v.to_owned())
|
|
|
|
} else {
|
|
|
|
Yaml::String(v.to_owned())
|
|
|
|
}
|
|
|
|
}
|
2015-05-30 14:39:50 +00:00
|
|
|
}
|
2015-05-24 18:16:28 +00:00
|
|
|
}
|
2015-05-24 17:34:18 +00:00
|
|
|
}
|
|
|
|
|
2015-05-24 18:16:28 +00:00
|
|
|
static BAD_VALUE: Yaml = Yaml::BadValue;
|
|
|
|
impl<'a> Index<&'a str> for Yaml {
|
|
|
|
type Output = Yaml;
|
|
|
|
|
|
|
|
fn index(&self, idx: &'a str) -> &Yaml {
|
2016-02-28 00:30:13 +00:00
|
|
|
let key = Yaml::String(idx.to_owned());
|
2015-05-24 18:16:28 +00:00
|
|
|
match self.as_hash() {
|
|
|
|
Some(h) => h.get(&key).unwrap_or(&BAD_VALUE),
|
2018-09-15 16:49:04 +00:00
|
|
|
None => &BAD_VALUE,
|
2015-05-24 18:16:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Index<usize> for Yaml {
|
|
|
|
type Output = Yaml;
|
|
|
|
|
|
|
|
fn index(&self, idx: usize) -> &Yaml {
|
2017-05-13 13:55:32 +00:00
|
|
|
if let Some(v) = self.as_vec() {
|
|
|
|
v.get(idx).unwrap_or(&BAD_VALUE)
|
|
|
|
} else if let Some(v) = self.as_hash() {
|
2024-02-08 06:12:14 +00:00
|
|
|
let key = Yaml::Integer(i64::try_from(idx).unwrap());
|
2017-05-13 13:55:32 +00:00
|
|
|
v.get(&key).unwrap_or(&BAD_VALUE)
|
|
|
|
} else {
|
|
|
|
&BAD_VALUE
|
2015-05-24 18:16:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-08 21:31:36 +00:00
|
|
|
impl IntoIterator for Yaml {
|
|
|
|
type Item = Yaml;
|
|
|
|
type IntoIter = YamlIter;
|
|
|
|
|
|
|
|
fn into_iter(self) -> Self::IntoIter {
|
2016-09-22 08:54:51 +00:00
|
|
|
YamlIter {
|
2023-08-11 23:54:46 +00:00
|
|
|
yaml: self.into_vec().unwrap_or_default().into_iter(),
|
2016-09-22 08:54:51 +00:00
|
|
|
}
|
2016-08-08 21:31:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-17 00:17:40 +00:00
|
|
|
/// An iterator over a [`Yaml`] node.
|
2016-08-08 21:31:36 +00:00
|
|
|
pub struct YamlIter {
|
2023-08-17 00:17:40 +00:00
|
|
|
yaml: std::vec::IntoIter<Yaml>,
|
2016-08-08 21:31:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Iterator for YamlIter {
|
|
|
|
type Item = Yaml;
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<Yaml> {
|
2016-08-08 22:21:57 +00:00
|
|
|
self.yaml.next()
|
2016-08-08 21:31:36 +00:00
|
|
|
}
|
|
|
|
}
|
2020-05-05 12:53:06 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use crate::YamlLoader;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_read_bom() {
|
|
|
|
let s = b"\xef\xbb\xbf---
|
|
|
|
a: 1
|
|
|
|
b: 2.2
|
|
|
|
c: [1, 2]
|
|
|
|
";
|
|
|
|
let out = YamlLoader::load_from_bytes(s as &[u8]).unwrap();
|
|
|
|
let doc = &out[0];
|
|
|
|
assert_eq!(doc["a"].as_i64().unwrap(), 1i64);
|
|
|
|
assert!((doc["b"].as_f64().unwrap() - 2.2f64).abs() <= f64::EPSILON);
|
|
|
|
assert_eq!(doc["c"][1].as_i64().unwrap(), 2i64);
|
|
|
|
assert!(doc["d"][0].is_badvalue());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_read_utf16le() {
|
|
|
|
let s = b"\xff\xfe-\x00-\x00-\x00
|
|
|
|
\x00a\x00:\x00 \x001\x00
|
|
|
|
\x00b\x00:\x00 \x002\x00.\x002\x00
|
|
|
|
\x00c\x00:\x00 \x00[\x001\x00,\x00 \x002\x00]\x00
|
|
|
|
\x00";
|
|
|
|
let out = YamlLoader::load_from_bytes(s as &[u8]).unwrap();
|
|
|
|
let doc = &out[0];
|
|
|
|
println!("GOT: {doc:?}");
|
|
|
|
assert_eq!(doc["a"].as_i64().unwrap(), 1i64);
|
|
|
|
assert!((doc["b"].as_f64().unwrap() - 2.2f64) <= f64::EPSILON);
|
|
|
|
assert_eq!(doc["c"][1].as_i64().unwrap(), 2i64);
|
|
|
|
assert!(doc["d"][0].is_badvalue());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_read_utf16be() {
|
|
|
|
let s = b"\xfe\xff\x00-\x00-\x00-\x00
|
|
|
|
\x00a\x00:\x00 \x001\x00
|
|
|
|
\x00b\x00:\x00 \x002\x00.\x002\x00
|
|
|
|
\x00c\x00:\x00 \x00[\x001\x00,\x00 \x002\x00]\x00
|
|
|
|
";
|
|
|
|
let out = YamlLoader::load_from_bytes(s as &[u8]).unwrap();
|
|
|
|
let doc = &out[0];
|
|
|
|
println!("GOT: {doc:?}");
|
|
|
|
assert_eq!(doc["a"].as_i64().unwrap(), 1i64);
|
|
|
|
assert!((doc["b"].as_f64().unwrap() - 2.2f64).abs() <= f64::EPSILON);
|
|
|
|
assert_eq!(doc["c"][1].as_i64().unwrap(), 2i64);
|
|
|
|
assert!(doc["d"][0].is_badvalue());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_read_utf16le_nobom() {
|
|
|
|
let s = b"-\x00-\x00-\x00
|
|
|
|
\x00a\x00:\x00 \x001\x00
|
|
|
|
\x00b\x00:\x00 \x002\x00.\x002\x00
|
|
|
|
\x00c\x00:\x00 \x00[\x001\x00,\x00 \x002\x00]\x00
|
|
|
|
\x00";
|
|
|
|
let out = YamlLoader::load_from_bytes(s as &[u8]).unwrap();
|
|
|
|
let doc = &out[0];
|
|
|
|
println!("GOT: {doc:?}");
|
|
|
|
assert_eq!(doc["a"].as_i64().unwrap(), 1i64);
|
|
|
|
assert!((doc["b"].as_f64().unwrap() - 2.2f64).abs() <= f64::EPSILON);
|
|
|
|
assert_eq!(doc["c"][1].as_i64().unwrap(), 2i64);
|
|
|
|
assert!(doc["d"][0].is_badvalue());
|
|
|
|
}
|
|
|
|
}
|