Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Change YAML crate to serde_yaml #474

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.toml
Expand Up @@ -17,7 +17,7 @@ maintenance = { status = "actively-developed" }
[features]
default = ["toml", "json", "yaml", "ini", "ron", "json5", "convert-case", "async"]
json = ["serde_json"]
yaml = ["yaml-rust"]
yaml = ["serde_yaml"]
ini = ["rust-ini"]
json5 = ["json5_rs", "serde/derive"]
convert-case = ["convert_case"]
Expand All @@ -32,7 +32,7 @@ nom = "7"
async-trait = { version = "0.1.50", optional = true }
toml = { version = "0.8", optional = true }
serde_json = { version = "1.0.2", optional = true }
yaml-rust = { version = "0.4", optional = true }
serde_yaml = { version = "0.9", optional = true }
rust-ini = { version = "0.19", optional = true }
ron = { version = "0.8", optional = true }
json5_rs = { version = "0.4", optional = true, package = "json5" }
Expand Down
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -18,7 +18,7 @@

[JSON]: https://github.com/serde-rs/json
[TOML]: https://github.com/toml-lang/toml
[YAML]: https://github.com/chyh1990/yaml-rust
[YAML]: https://github.com/dtolnay/serde-yaml
[INI]: https://github.com/zonyitoo/rust-ini
[RON]: https://github.com/ron-rs/ron
[JSON5]: https://github.com/callum-oakley/json5-rs
Expand Down
2 changes: 1 addition & 1 deletion src/file/format/mod.rs
Expand Up @@ -40,7 +40,7 @@ pub enum FileFormat {
#[cfg(feature = "json")]
Json,

/// YAML (parsed with yaml_rust)
/// YAML (parsed with serde_yaml)
#[cfg(feature = "yaml")]
Yaml,

Expand Down
121 changes: 38 additions & 83 deletions src/file/format/yaml.rs
@@ -1,8 +1,4 @@
use std::error::Error;
use std::fmt;
use std::mem;

use yaml_rust as yaml;

use crate::format;
use crate::map::Map;
Expand All @@ -12,94 +8,53 @@ pub fn parse(
uri: Option<&String>,
text: &str,
) -> Result<Map<String, Value>, Box<dyn Error + Send + Sync>> {
// Parse a YAML object from file
let mut docs = yaml::YamlLoader::load_from_str(text)?;
let root = match docs.len() {
0 => yaml::Yaml::Hash(yaml::yaml::Hash::new()),
1 => mem::replace(&mut docs[0], yaml::Yaml::Null),
n => {
return Err(Box::new(MultipleDocumentsError(n)));
}
};

let value = from_yaml_value(uri, &root)?;
// Parse a YAML input from the provided text
let value = from_yaml_value(uri, serde_yaml::from_str(text)?);
format::extract_root_table(uri, value)
}

fn from_yaml_value(
uri: Option<&String>,
value: &yaml::Yaml,
) -> Result<Value, Box<dyn Error + Send + Sync>> {
match *value {
yaml::Yaml::String(ref value) => Ok(Value::new(uri, ValueKind::String(value.clone()))),
yaml::Yaml::Real(ref value) => {
// TODO: Figure out in what cases this can panic?
value
.parse::<f64>()
.map_err(|_| {
Box::new(FloatParsingError(value.to_string())) as Box<(dyn Error + Send + Sync)>
})
.map(ValueKind::Float)
.map(|f| Value::new(uri, f))
}
yaml::Yaml::Integer(value) => Ok(Value::new(uri, ValueKind::I64(value))),
yaml::Yaml::Boolean(value) => Ok(Value::new(uri, ValueKind::Boolean(value))),
yaml::Yaml::Hash(ref table) => {
let mut m = Map::new();
for (key, value) in table {
match key {
yaml::Yaml::String(k) => m.insert(k.to_owned(), from_yaml_value(uri, value)?),
yaml::Yaml::Integer(k) => m.insert(k.to_string(), from_yaml_value(uri, value)?),
_ => unreachable!(),
};
pub fn from_yaml_value(uri: Option<&String>, value: serde_yaml::Value) -> Value {
let vk = match value {
serde_yaml::Value::Tagged(_) | serde_yaml::Value::Null => ValueKind::Nil,
serde_yaml::Value::Bool(v) => ValueKind::Boolean(v),
serde_yaml::Value::String(v) => ValueKind::String(v),

serde_yaml::Value::Number(v) => {
if v.is_i64() {
ValueKind::I64(v.as_i64().expect("i64"))
} else if v.is_u64() {
ValueKind::U64(v.as_u64().expect("u64"))
} else if v.is_f64() {
ValueKind::Float(v.as_f64().expect("f64"))
} else {
ValueKind::Nil
}
Ok(Value::new(uri, ValueKind::Table(m)))
}
yaml::Yaml::Array(ref array) => {
let mut l = Vec::new();

for value in array {
l.push(from_yaml_value(uri, value)?);
}
serde_yaml::Value::Mapping(table) => {
let m = table
.into_iter()
.map(|(k, v)| {
let key = match k {
serde_yaml::Value::Number(v) => v.to_string(),
serde_yaml::Value::String(v) => v,

_ => unreachable!(),
};
let value = from_yaml_value(uri, v);
(key, value)
})
.collect();

Ok(Value::new(uri, ValueKind::Array(l)))
ValueKind::Table(m)
}

// 1. Yaml NULL
// 2. BadValue – It shouldn't be possible to hit BadValue as this only happens when
// using the index trait badly or on a type error but we send back nil.
// 3. Alias – No idea what to do with this and there is a note in the lib that its
// not fully supported yet anyway
_ => Ok(Value::new(uri, ValueKind::Nil)),
}
}
serde_yaml::Value::Sequence(array) => {
let l = array.into_iter().map(|v| from_yaml_value(uri, v)).collect();

#[derive(Debug, Copy, Clone)]
struct MultipleDocumentsError(usize);

impl fmt::Display for MultipleDocumentsError {
fn fmt(&self, format: &mut fmt::Formatter) -> fmt::Result {
write!(format, "Got {} YAML documents, expected 1", self.0)
}
}

impl Error for MultipleDocumentsError {
fn description(&self) -> &str {
"More than one YAML document provided"
}
}

#[derive(Debug, Clone)]
struct FloatParsingError(String);

impl fmt::Display for FloatParsingError {
fn fmt(&self, format: &mut fmt::Formatter) -> fmt::Result {
write!(format, "Parsing {} as floating point number failed", self.0)
}
}
ValueKind::Array(l)
}
};

impl Error for FloatParsingError {
fn description(&self) -> &str {
"Floating point number parsing failed"
}
Value::new(uri, vk)
}
5 changes: 3 additions & 2 deletions tests/file_yaml.rs
Expand Up @@ -81,12 +81,13 @@ fn test_error_parse() {

let path_with_extension: PathBuf = ["tests", "Settings-invalid.yaml"].iter().collect();

// Should fail to parse block mapping as no `:` exists to identify a key
assert!(res.is_err());
assert_eq!(
res.unwrap_err().to_string(),
format!(
"while parsing a block mapping, did not find expected key at \
line 2 column 1 in {}",
"could not find expected ':' at line 3 column 1, \
while scanning a simple key at line 2 column 1 in {}",
path_with_extension.display()
)
);
Expand Down
5 changes: 3 additions & 2 deletions tests/legacy/file_yaml.rs
Expand Up @@ -81,12 +81,13 @@ fn test_error_parse() {

let path_with_extension: PathBuf = ["tests", "Settings-invalid.yaml"].iter().collect();

// Should fail to parse block mapping as no `:` exists to identify a key
assert!(res.is_err());
assert_eq!(
res.unwrap_err().to_string(),
format!(
"while parsing a block mapping, did not find expected key at \
line 2 column 1 in {}",
"could not find expected ':' at line 3 column 1, \
while scanning a simple key at line 2 column 1 in {}",
path_with_extension.display()
)
);
Expand Down