jsonata-rs 0.3.4

An (incomplete) implementation of JSONata in Rust
Documentation
use glob::glob;
use std::env;
use std::fs;
use std::io::Write;
use std::path::Path;

fn main() {
    println!("cargo:rerun-if-changed=tests/testsuite/**/*.json");
    println!("cargo:rerun-if-changed=tests/customsuite/**/*.json");

    let out_dir = env::var("OUT_DIR").unwrap();
    let dest_path = Path::new(&out_dir).join("generated_tests.rs");
    let mut file = fs::File::create(dest_path).unwrap();

    let resources = {
        let mut r = get_test_resources("tests/testsuite/**/*.json");
        r.extend(get_test_resources("tests/customsuite/**/*.json"));
        r
    };

    for resource in resources {
        if resource.contains("/skip/") {
            writeln!(
                file,
                r#"
                #[test]
                #[ignore]
                fn test_{}() {{
                    test_case(r"{}");
                }}
                "#,
                sanitize_filename(&resource),
                resource
            )
            .unwrap();
        } else {
            writeln!(
                file,
                r#"
                #[test]
                fn test_{}() {{
                    test_case(r"{}");
                }}
                "#,
                sanitize_filename(&resource),
                resource
            )
            .unwrap();
        }
    }
}

fn get_test_resources(pattern: &str) -> Vec<String> {
    glob(pattern)
        .expect("Failed to read glob pattern")
        .filter_map(Result::ok)
        .filter(|path| !path.to_string_lossy().contains("datasets")) // Exclude datasets folder
        .map(|path| path.to_string_lossy().into_owned())
        .collect()
}

fn sanitize_filename(filename: &str) -> String {
    let mut sanitized = String::new();
    let mut prev_was_underscore = false;

    for c in filename.chars() {
        if c.is_alphanumeric() {
            if prev_was_underscore {
                sanitized.push('_');
                prev_was_underscore = false;
            }
            sanitized.push(c.to_ascii_lowercase());
        } else {
            prev_was_underscore = true;
        }
    }

    sanitized
}