1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
use std::convert::TryFrom; use std::iter::FromIterator; use indexmap::IndexMap; use serde_yaml as yaml; use snafu::{ensure, Snafu}; use super::FileSource; use crate::Value; #[derive(Clone, Debug)] pub struct Yaml {} impl FileSource for Yaml { type Value = yaml::Value; type SerError = yaml::Error; type DeError = yaml::Error; fn extension(&self) -> String { "yaml".into() } fn deserialize(&self, string: &str) -> Result<Self::Value, Self::DeError> { yaml::from_str(&string) } fn serialize(&self, value: &Self::Value) -> Result<String, Self::SerError> { let mut string = yaml::to_string(&value)?; string.push('\n'); Ok(string) } } #[derive(Debug, Snafu)] #[snafu(visibility(pub(crate)))] pub enum FromYamlError { #[snafu(display("Unable to convert Yaml with non-string key: {:#?}", key))] NonStringKey { key: yaml::Value }, } impl TryFrom<yaml::Value> for Value { type Error = FromYamlError; fn try_from(value: yaml::Value) -> Result<Value, Self::Error> { match value { yaml::Value::Null => Ok(Value::Null), yaml::Value::Bool(bool) => Ok(Value::Bool(bool)), yaml::Value::Number(number) => Ok(if number.is_u64() { Value::Uint(number.as_u64().unwrap()) } else if number.is_i64() { Value::Int(number.as_i64().unwrap()) } else { Value::Float(number.as_f64().unwrap()) }), yaml::Value::String(string) => Ok(Value::String(string)), yaml::Value::Sequence(sequence) => { let mut next_array = Vec::with_capacity(sequence.len()); sequence .into_iter() .try_for_each(|item| -> Result<(), FromYamlError> { let next_item = Self::try_from(item)?; next_array.push(next_item); Ok(()) })?; Ok(Value::Array(next_array)) } yaml::Value::Mapping(mapping) => { let mut next_map = IndexMap::with_capacity(mapping.len()); mapping .into_iter() .try_for_each(|(key, value)| -> Result<(), FromYamlError> { ensure!(key.is_string(), NonStringKey { key: key.clone() }); let key = key.as_str().unwrap().to_owned(); let next_value = Self::try_from(value)?; next_map.insert(key, next_value); Ok(()) })?; Ok(Value::Object(next_map)) } } } } impl From<Value> for yaml::Value { fn from(value: Value) -> yaml::Value { match value { Value::Null => yaml::Value::Null, Value::Bool(bool) => yaml::Value::Bool(bool), Value::Int(int) => yaml::Value::Number(int.into()), Value::Uint(uint) => yaml::Value::Number(uint.into()), Value::Float(float) => yaml::Value::Number(float.into()), Value::String(string) => yaml::Value::String(string), Value::Array(array) => { yaml::Value::Sequence(Vec::from_iter(array.into_iter().map(Self::from))) } Value::Object(object) => yaml::Value::Mapping(yaml::Mapping::from_iter( object .into_iter() .map(|(key, value)| (Self::from(key), Self::from(value))), )), } } }