use crate::error::Result;
use crate::error::Error;
pub struct Restructurer<'r> {
recursive: bool,
ignore: Vec<&'r str>,
}
impl<'r> Restructurer<'r> {
pub fn new() -> Self {
Restructurer {
recursive: true,
ignore: vec![],
}
}
pub fn recursive(mut self, recursive: bool) -> Self {
self.recursive = recursive;
self
}
pub fn ignore (mut self, ignore: Vec<&'r str>) -> Self {
self.ignore = ignore;
self
}
pub fn apply_value(self: &Self, value: &mut serde_yaml::Value) -> Result<()> {
use serde_yaml::Value;
let m = value.as_mapping_mut()
.ok_or(Error::Restructure("not a mapping".into()))?;
let dotted_keys: Vec<String> = m.keys()
.filter(|v| {
let mut res = false;
if let Value::String(ref s) = v {
if let Some(c) = s.find('.') {
if c > 0 && c < s.len() - 1 {
res = true;
}
}
}
res
})
.map(|v| v.as_str()
.unwrap()
.to_owned())
.collect();
for k in dotted_keys {
self.restructure_key(m, &k)?;
}
if self.recursive {
let map_keys: Vec<String> = m.iter()
.filter(|(k, v)| k.is_string() && v.is_mapping())
.map(|(k, _)| k.as_str().unwrap().to_owned())
.collect();
for k in map_keys {
self.apply_value(m.get_mut(&k)
.unwrap())?;
}
}
Ok(())
}
pub fn apply_str(self: &Self, s: &str) -> Result<serde_yaml::Value> {
let mut value = serde_yaml::from_str(s)?;
self.apply_value(&mut value)?;
Ok(value)
}
fn restructure_key(self: &Self, m: &mut serde_yaml::Mapping, k: &str) -> Result<()> {
use serde_yaml::Value;
if let Some((mut prefix, mut suffix)) = k.split_once('.') {
for i in &self.ignore {
if k.starts_with(i) {
if let Some((p, s)) = k.split_once(&format!("{i}.")) {
prefix = i;
suffix = s;
break;
} else {
return Ok(());
}
}
}
if prefix.is_empty() || suffix.is_empty() {
return Ok(());
}
let val = m.remove(&k).unwrap();
if !m.contains_key(prefix) {
m.insert(Value::String(prefix.into()),
Value::Mapping(serde_yaml::Mapping::new()));
}
let inner = m.get_mut(prefix)
.unwrap()
.as_mapping_mut()
.ok_or(Error::Restructure(format!("could not insert key {k}: {prefix} is not a mapping")))?;
inner.insert(Value::String(suffix.into()),
val);
self.restructure_key(inner, suffix)?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::{assert_eq, assert_ne};
use serde_yaml::Value;
#[test]
fn test_simple() {
let s1 = r#"
foo:
bar:
baz: true
"#;
let s2 = r#"
foo.bar.baz: true
"#;
let mut v1: Value = serde_yaml::from_str(s1).unwrap();
let mut v2: Value = serde_yaml::from_str(s2).unwrap();
let mut r = Restructurer::new()
.recursive(false);
r.apply_value(&mut v2).unwrap();
assert_eq!(v1, v2);
v2 = serde_yaml::from_str(s2).unwrap();
r = r.recursive(true);
r.apply_value(&mut v2).unwrap();
assert_eq!(v1, v2);
r.apply_value(&mut v1).unwrap();
assert_eq!(v1, v2);
}
#[test]
fn test_recursion1() {
let s1 = r#"
foo:
bar:
baz: true
"#;
let s2 = r#"
foo:
bar.baz: true
"#;
let v1: Value = serde_yaml::from_str(s1).unwrap();
let mut v2: Value = serde_yaml::from_str(s2).unwrap();
Restructurer::new()
.recursive(false)
.apply_value(&mut v2).unwrap();
assert_ne!(v1, v2);
v2 = serde_yaml::from_str(s2).unwrap();
Restructurer::new()
.apply_value(&mut v2).unwrap();
assert_eq!(v1, v2);
}
#[test]
fn test_error() {
let s1 = r#"
foo:
bar: 42
foo.bar.baz: true
"#;
let mut v1: Value = serde_yaml::from_str(s1).unwrap();
let res = Restructurer::new()
.apply_value(&mut v1);
assert!(res.is_err());
}
#[test]
fn test_apply_str() {
let s1 = r#"
foo:
bar:
baz: true
"#;
let s2 = r#"
foo.bar.baz: true
"#;
let v1: Value = serde_yaml::from_str(s1).unwrap();
let v2: Value = Restructurer::new()
.apply_str(&s2)
.unwrap();
assert_eq!(v1, v2);
}
#[test]
fn ignore() {
let s1 = r#"
foo:
ignored.key:
baz: true
"#;
let s2 = r#"
foo.ignored.key.baz: true
"#;
let v1: Value = serde_yaml::from_str(s1).unwrap();
let v2: Value = Restructurer::new()
.ignore(vec!["ignored.key"])
.apply_str(&s2)
.unwrap();
assert_eq!(v1, v2);
}
}