edge-schema 0.1.0

Shared schema types for Wasmer Edge.
Documentation
//! Generates JSON schemas  for the workload and token Rust types.

fn main() {
    codegen::generate_schemas();
}

mod codegen {
    use std::{collections::HashMap, path::Path};

    pub fn generate_schemas() {
        eprintln!("Generating schemas...");

        let dir = schema_dir();

        // JSON Schema
        let json_dir = dir.join("jsonschema");
        generate_jsonschema(&json_dir);

        eprintln!("Schema-generation complete");
    }

    fn generate_jsonschema(dir: &Path) {
        // Generate .schema.json files.

        let types_dir = dir.join("types");
        eprintln!("Writing .json.schema files to '{}'", types_dir.display());

        std::fs::create_dir_all(&types_dir).unwrap();
        let schemas = build_jsonschema_map();
        for (filename, content) in schemas {
            std::fs::write(types_dir.join(filename), content).unwrap();
        }

        // Markdown docs.

        // Sadly doesn't work correctly with the generated schema.
        // eprintln!("Generating markdown docs at '{}'", markdown_dir.display());
        // let markdown_dir = dir.join("markdown");
        // std::fs::create_dir_all(&markdown_dir).unwrap();
        // let (cmd, args) = if command_exists("jsonschema2md") {
        //     ("jsonschema2md", vec![])
        // } else if command_exists("npx") {
        //     ("npx", vec!["--yes", "@adobe/jsonschema2md"])
        // } else {
        //     panic!("Could not find the 'jsonschema2md' or 'npx' command. Install it with 'npm install -g jsonschema2md'.");
        // };

        // let status = Command::new(cmd)
        //     .args(args)
        //     .arg("-d")
        //     .arg(types_dir)
        //     .arg("-o")
        //     .arg(markdown_dir)
        //     .spawn()
        //     .expect("failed to run jsonschema2md")
        //     .wait()
        //     .expect("Failed to run jsonschema2md");
        // if !status.success() {
        //     panic!("jsonschema2md failed");
        // }

        eprintln!("JSON schema generation complete");
    }

    /// Returns a map of filename to serialized JSON schema.
    fn build_jsonschema_map() -> HashMap<String, String> {
        let mut map = HashMap::new();

        fn add_schema<T: schemars::JsonSchema>(map: &mut HashMap<String, String>, name: &str) {
            let gen =
                schemars::gen::SchemaGenerator::new(schemars::gen::SchemaSettings::draft2019_09());
            map.insert(
                format!("{name}.schema.json"),
                serde_json::to_string_pretty(&gen.into_root_schema_for::<T>()).unwrap(),
            );
        }

        add_schema::<edge_schema::schema::EntityMeta>(&mut map, "EntityMeta");
        add_schema::<edge_schema::schema::AppV1Spec>(&mut map, "AppV1Spec");
        add_schema::<edge_schema::schema::AppConfigV1>(&mut map, "AppConfigV1");

        map
    }

    /// Get the local path to the directory where generated schemas are stored.
    fn schema_dir() -> std::path::PathBuf {
        let crate_dir =
            std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR env var to be set");
        let root_dir = std::path::Path::new(&crate_dir)
            .parent()
            .unwrap()
            .parent()
            .unwrap();

        let schema_dir = root_dir.join("docs/schema/generated");
        if !schema_dir.is_dir() {
            panic!("Expected the {} directory to exist", schema_dir.display());
        }

        schema_dir
    }

    // fn command_exists(name: &str) -> bool {
    //     std::process::Command::new("which")
    //         .arg(name)
    //         .output()
    //         .map(|output| output.status.success())
    //         .unwrap_or(false)
    // }

    /// Tests that the generated schemas are still up to date.
    #[test]
    fn test_generated_schemas_up_to_date() {
        let dir = schema_dir();

        let jsonschema = build_jsonschema_map();
        let json_dir = dir.join("jsonschema").join("types");

        for (filename, jsonschema) in &jsonschema {
            let path = json_dir.join(filename);
            let contents = std::fs::read_to_string(&path).unwrap();

            if contents != *jsonschema {
                panic!(
                    "Auto-generated OpenaAPI schema at '{}' is not up to date!\n\
                    Run `cargo run -p edge-schema --bin generate-core-schemas` to update it.",
                    path.display()
                );
            }
        }

        for res in std::fs::read_dir(&json_dir).unwrap() {
            let entry = res.unwrap();

            let file_name = entry
                .file_name()
                .to_str()
                .expect("non-utf8 filename")
                .to_string();

            if !jsonschema.contains_key(&file_name) {
                panic!(
                    "Found unexpected file in the json schemas directory: '{}' - delete it!",
                    entry.path().display(),
                );
            }
        }
    }
}