harmony-jolt 0.7.0

JSON to JSON transformation library using the JOLT spec
Documentation
use std::fs;
use std::path::PathBuf;
use harmony_jolt::{TransformSpec, transform};
use serde::Deserialize;
use serde_json::Value as JsonValue;

fn iter_json(dir_path: &str) -> Box<dyn Iterator<Item = (PathBuf, TestCase)>> {
    let dir = fs::read_dir(dir_path).unwrap();

    let iter = dir.into_iter().map(|entry| {
        let path = entry.unwrap().path();
        let contents = fs::read_to_string(&path).unwrap();
        let contents = contents
            .split('\n')
            .map(|line| {
                let mut in_str = false;
                let mut maybe_comment = false;
                for (idx, c) in line.char_indices() {
                    match c {
                        '"' => {
                            in_str = !in_str;
                            maybe_comment = false;
                        }
                        '/' => {
                            if maybe_comment {
                                return &line[..idx - 1];
                            }
                            maybe_comment = !in_str;
                        }
                        _ => maybe_comment = false,
                    }
                }

                line
            })
            .collect::<Vec<_>>()
            .join("");
        let json = match serde_json::from_str::<TestCase>(&contents) {
            Ok(json) => json,
            Err(e) => {
                let path = path.to_str().unwrap();
                panic!("failed to deserialize test case at {path}:\n{e}\ninput was:\n{contents}");
            }
        };

        (path, json)
    });

    Box::new(iter)
}

pub fn test_dir(dir_path: &str, operation: &str, skiplist: &[&str]) {
    for (path, case) in iter_json(dir_path) {
        let path = path.to_str().unwrap();

        if skiplist.iter().any(|s| path.contains(s)) {
            continue;
        }

        let val = serde_json::json!([{
            "operation": operation,
            "spec": case.spec,
        }]);

        let spec: TransformSpec = match serde_json::from_value(val) {
            Ok(json) => json,
            Err(e) => {
                panic!("failed to deserialize test case at {path}.\n{e}");
            }
        };

        let output = match transform(case.input, &spec) {
            Ok(output) => output,
            Err(e) => {
                panic!("failed test;operation={operation};path={path};error={e}");
            }
        };

        if output != case.expected {
            let expected = serde_json::to_string_pretty(&case.expected).unwrap();
            let output = serde_json::to_string_pretty(&output).unwrap();
            panic!("failed test;operation={operation};path={path};\nexpected={expected}\noutput={output}");
        }
    }
}

#[derive(Deserialize)]
struct TestCase {
    input: JsonValue,
    spec: JsonValue,
    expected: JsonValue,
}