use std::{fs, path::PathBuf};
use crate::checkers::base::CheckError;
use super::generic::{Array, Mapping, MappingError, Value};
pub(crate) fn from_path(path: PathBuf) -> Result<Box<dyn Mapping>, CheckError> {
let file_contents = fs::read_to_string(path)?;
from_string(&file_contents)
}
pub(crate) fn from_string(
doc: &str,
) -> Result<Box<dyn Mapping>, crate::checkers::base::CheckError> {
if doc.trim().is_empty() {
return Ok(Box::new(serde_yaml_ng::Mapping::new()));
}
let doc: serde_yaml_ng::Value =
serde_yaml_ng::from_str(doc).map_err(|e| CheckError::InvalidFileFormat(e.to_string()))?;
Ok(Box::new(
doc.as_mapping()
.ok_or(CheckError::InvalidFileFormat("No object".to_string()))?
.clone(),
))
}
impl Mapping for serde_yaml_ng::Mapping {
fn to_string(&self, _indent: usize) -> Result<String, CheckError> {
if self.is_empty() {
return Ok("".to_string());
}
Ok(serde_yaml_ng::to_string(&self).unwrap())
}
fn contains_key(&self, key: &str) -> bool {
self.contains_key(key)
}
fn get_mapping(
&mut self,
key: &str,
create_missing: bool,
) -> Result<&mut dyn Mapping, MappingError> {
if !self.contains_key(key) {
if !create_missing {
return Err(MappingError::MissingKey(key.to_string()));
}
self.insert(
serde_yaml_ng::value::Value::String(key.to_string()),
serde_yaml_ng::value::Value::Mapping(serde_yaml_ng::Mapping::new()),
);
}
let value = self.get_mut(key).unwrap();
if !value.is_mapping() {
Err(MappingError::WrongType(format!("{key} is not a mapping")))
} else {
Ok(value.as_mapping_mut().unwrap())
}
}
fn get_array(
&mut self,
key: &str,
create_missing: bool,
) -> Result<&mut dyn Array, MappingError> {
if !self.contains_key(key) {
if !create_missing {
return Err(MappingError::MissingKey(key.to_string()));
}
self.insert(
serde_yaml_ng::value::Value::String(key.to_string()),
serde_yaml_ng::value::Value::Sequence(serde_yaml_ng::Sequence::new()),
);
}
let value = self.get_mut(key).unwrap();
if !value.is_sequence() {
Err(MappingError::WrongType(format!("{key} is not an array")))
} else {
Ok(value.as_sequence_mut().unwrap())
}
}
fn get_string(&self, key: &str) -> Result<String, MappingError> {
if !self.contains_key(key) {
return Err(MappingError::MissingKey(key.to_string()));
}
let value = self.get(key).unwrap();
if !value.is_string() {
Err(MappingError::WrongType(format!("{key} is not a string")))
} else {
Ok(value.as_str().unwrap().to_string())
}
}
fn insert(&mut self, key: &toml_edit::Key, value: &toml_edit::Item) {
self.insert(
serde_yaml_ng::value::Value::String(key.to_string()),
serde_yaml_ng::Value::from_toml_value(value),
);
}
fn remove(&mut self, key: &str) {
if self.contains_key(key) {
self.remove(key);
}
}
}
impl Array for serde_yaml_ng::value::Sequence {
fn insert_when_not_present(&mut self, value: &toml_edit::Item) {
let value = serde_yaml_ng::value::Value::from_toml_value(value);
if !self.contains(&value) {
self.push(value);
}
}
fn remove(&mut self, value: &toml_edit::Item) {
let value = serde_yaml_ng::value::Value::from_toml_value(value);
let array = self;
for (idx, array_item) in array.iter().enumerate() {
if *array_item == value {
array.remove(idx);
return;
}
}
}
fn contains_item(&self, value: &toml_edit::Item) -> bool {
let value = serde_yaml_ng::value::Value::from_toml_value(value);
self.contains(&value)
}
}
impl Value for serde_yaml_ng::value::Value {
fn from_toml_value(value: &toml_edit::Item) -> serde_yaml_ng::value::Value {
match value {
toml_edit::Item::Value(toml_edit::Value::String(v)) => {
serde_yaml_ng::value::Value::String(v.value().to_owned())
}
toml_edit::Item::Value(toml_edit::Value::Integer(v)) => {
serde_yaml_ng::value::Value::Number(serde_yaml_ng::Number::from(
v.value().to_owned(),
))
}
toml_edit::Item::Value(toml_edit::Value::Float(v)) => {
serde_yaml_ng::value::Value::Number(serde_yaml_ng::Number::from(
v.value().to_owned(),
))
}
toml_edit::Item::Value(toml_edit::Value::Boolean(v)) => {
serde_yaml_ng::value::Value::Bool(v.value().to_owned())
}
toml_edit::Item::Value(toml_edit::Value::Datetime(v)) => {
serde_yaml_ng::value::Value::String(v.to_string())
}
toml_edit::Item::Value(toml_edit::Value::Array(v)) => {
let mut a = vec![];
for v_item in v {
a.push(serde_yaml_ng::value::Value::from_toml_value(
&toml_edit::Item::Value(v_item.to_owned()),
))
}
serde_yaml_ng::value::Value::Sequence(a)
}
toml_edit::Item::Table(v) => {
let mut a: serde_yaml_ng::value::Mapping = serde_yaml_ng::value::Mapping::new();
for (k, v_item) in v {
a.insert(
serde_yaml_ng::value::Value::String(k.to_owned()),
serde_yaml_ng::value::Value::from_toml_value(v_item),
);
}
serde_yaml_ng::value::Value::Mapping(a)
}
toml_edit::Item::Value(toml_edit::Value::InlineTable(v)) => {
let mut a: serde_yaml_ng::value::Mapping = serde_yaml_ng::value::Mapping::new();
for (k, v_item) in v {
a.insert(
serde_yaml_ng::value::Value::String(k.to_owned()),
serde_yaml_ng::value::Value::from_toml_value(&toml_edit::Item::Value(
v_item.to_owned(),
)),
);
}
serde_yaml_ng::value::Value::Mapping(a)
}
toml_edit::Item::ArrayOfTables(v) => {
let mut a = vec![];
for v_item in v {
a.push(serde_yaml_ng::value::Value::from_toml_value(
&toml_edit::Item::Table(v_item.to_owned()),
))
}
serde_yaml_ng::value::Value::Sequence(a)
}
_ => serde_yaml_ng::value::Value::Null,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use super::super::generic::tests::get_test_table;
use super::super::generic::tests::test_mapping;
#[test]
fn test_access_map() {
let table = get_test_table();
let binding = serde_yaml_ng::Value::from_toml_value(&table);
let mut mapping_to_check = binding.as_mapping().unwrap().to_owned();
test_mapping(Box::new(mapping_to_check.clone()));
assert_eq!(
mapping_to_check
.get_mapping("dict", false)
.expect("")
.to_string(4)
.unwrap(),
"str: string\nint: 1\nfloat: 1.1\nbool: true\narray:\n- 1\n- 2\n".to_string()
);
}
#[test]
fn test_from_toml_value() {
let table = get_test_table();
let yaml_table = serde_yaml_ng::Value::from_toml_value(&table);
assert_eq!(
serde_yaml_ng::to_string(&yaml_table).unwrap(),
"str: string\nint: 1\nfloat: 1.1\nbool: true\narray:\n- 1\n- 2\ndict:\n str: string\n int: 1\n float: 1.1\n bool: true\n array:\n - 1\n - 2\n"
);
}
}