container-device-interface 0.1.2

CDI (Container Device Interface), is a specification, for container-runtimes, to support third-party devices.
Documentation
use serde::Serialize;
use serde_json;
use serde_yaml;
use std::error::Error;
use std::fmt::Write;
use std::path::Path;

pub fn choose_format(format: &str, path: &str) -> String {
    let mut format = format.to_string();
    if format.is_empty() {
        if let Some(ext) = Path::new(path).extension() {
            if ext == "json" || ext == "yaml" {
                format = ext.to_string_lossy().to_string();
            }
        }
    }
    format
}

pub fn marshal_object<T: Serialize>(level: usize, obj: &T, format: &str) -> String {
    let raw_result: Result<String, Box<dyn Error>> = if format == "json" {
        serde_json::to_string_pretty(obj).map_err(|e| Box::new(e) as Box<dyn Error>)
    } else {
        serde_yaml::to_string(obj).map_err(|e| Box::new(e) as Box<dyn Error>)
    };

    match raw_result {
        Ok(data) => {
            let mut out = String::new();
            for line in data.lines() {
                writeln!(out, "{}{}", &indent(level), line).unwrap();
            }
            out
        }
        Err(err) => format!("{}<failed to dump object: {:?}\n", indent(level), err),
    }
}

pub fn indent(level: usize) -> String {
    format!("{:width$}", "", width = level)
}

#[cfg(test)]
mod tests {
    use super::*;
    use pretty_assertions::assert_eq;
    use serde::Serialize;

    #[derive(Serialize)]
    struct TestObjMarshal {
        name: String,
        index: u32,
    }

    #[derive(Serialize)]
    struct NestedObjMarshal {
        outer_name: String,
        inner: TestObjMarshal,
    }

    #[test]
    fn test_marshal_object_json() {
        let obj = TestObjMarshal {
            name: String::from("TestJson"),
            index: 30,
        };
        let expected = r#"{
  "name": "TestJson",
  "index": 30
}"#;
        let result = marshal_object(0, &obj, "json");
        assert_eq!(result.trim(), expected);
    }

    #[test]
    fn test_marshal_object_yaml() {
        let obj = TestObjMarshal {
            name: String::from("TestYaml"),
            index: 30,
        };
        let expected = r#"name: TestYaml
index: 30"#;
        let result = marshal_object(0, &obj, "yaml");
        assert_eq!(result.trim(), expected);
    }

    #[test]
    fn test_nested_marshal_object_json() {
        let obj = NestedObjMarshal {
            outer_name: String::from("Outer"),
            inner: TestObjMarshal {
                name: String::from("Inner"),
                index: 25,
            },
        };
        let expected = r#"{
  "outer_name": "Outer",
  "inner": {
    "name": "Inner",
    "index": 25
  }
}"#;
        let result = marshal_object(0, &obj, "json");
        assert_eq!(result.trim(), expected);
    }

    #[test]
    fn test_nested_marshal_object_yaml() {
        let obj = NestedObjMarshal {
            outer_name: String::from("Outer"),
            inner: TestObjMarshal {
                name: String::from("Inner"),
                index: 25,
            },
        };
        let expected = r#"outer_name: Outer
inner:
  name: Inner
  index: 25"#;

        let result = marshal_object(0, &obj, "yaml");
        assert_eq!(result.trim(), expected);
    }

    #[test]
    fn test_marshal_object_json_with_indent() {
        let obj = TestObjMarshal {
            name: String::from("TestJson"),
            index: 20,
        };
        let expected = r#"{
   "name": "TestJson",
   "index": 20
 }"#;
        let result = marshal_object(1, &obj, "json");
        assert_eq!(result.trim(), expected);
    }

    #[test]
    fn test_marshal_object_yaml_with_indent() {
        let obj = TestObjMarshal {
            name: String::from("TestYaml"),
            index: 30,
        };
        let expected = r#"name: TestYaml
 index: 30"#;
        let result = marshal_object(1, &obj, "yaml");
        assert_eq!(result.trim(), expected);
    }

    #[test]
    fn test_marshal_object_json_without_indent() {
        let obj = TestObjMarshal {
            name: String::from("TestJson"),
            index: 10,
        };
        let result = marshal_object(0, &obj, "json");
        let expected = r#"
{
  "name": "TestJson",
  "index": 10
}
"#;
        assert_eq!(result.trim(), expected.trim());
    }

    #[test]
    fn test_marshal_object_yaml_without_indent() {
        let obj = TestObjMarshal {
            name: String::from("TestYaml"),
            index: 35,
        };
        let result = marshal_object(0, &obj, "yaml");
        let expected = r#"
name: TestYaml
index: 35
"#;
        assert_eq!(result.trim(), expected.trim());
    }
}