rototo 0.1.0-alpha.8

Control plane for runtime configuration of your application.
Documentation
use std::sync::Arc;

use super::super::catalog_schema::catalog_schema_uri;
use super::super::index::*;
use super::super::source::SourceDocument;
use super::super::syntax::{ParsedToml, SyntaxIndex, table_location};
use super::fields::json_from_toml_value;

pub(crate) fn project_catalog(
    document: &SourceDocument,
    syntax: &SyntaxIndex,
    id: &str,
) -> CatalogNode {
    let json = syntax.json.get(&document.id).cloned();

    CatalogNode {
        doc: document.id,
        id: id.to_owned(),
        path: document.path.clone(),
        location: document.document_location(),
        json,
        validator: None,
        invalid_message: None,
    }
}

pub(crate) fn compile_catalog_validators(index: &mut SemanticIndex) {
    let resources = catalog_schema_resources(index);
    for catalog in index.catalogs.values_mut() {
        let Some(json) = catalog.json.as_ref() else {
            continue;
        };
        let base_uri = catalog_schema_uri(&catalog.id);
        match jsonschema::options()
            .should_validate_formats(true)
            .with_base_uri(base_uri)
            .with_resources(resources.clone().into_iter())
            .build(json)
        {
            Ok(validator) => {
                catalog.validator = Some(Arc::new(validator));
                catalog.invalid_message = None;
            }
            Err(err) => {
                catalog.validator = None;
                catalog.invalid_message = Some(err.to_string());
            }
        }
    }
}

fn catalog_schema_resources(index: &SemanticIndex) -> Vec<(String, jsonschema::Resource)> {
    let mut resources = Vec::new();
    for catalog in index.catalogs.values() {
        let Some(json) = catalog.json.as_ref() else {
            continue;
        };
        let Ok(resource) = jsonschema::Resource::from_contents(json.clone()) else {
            continue;
        };

        resources.push((catalog_schema_uri(&catalog.id), resource.clone()));
        if let Some(id) = json
            .get("$id")
            .and_then(serde_json::Value::as_str)
            .map(normalize_schema_uri)
            .filter(|id| !id.is_empty())
        {
            resources.push((id, resource));
        }
    }
    resources
}

fn normalize_schema_uri(uri: &str) -> String {
    uri.trim_end_matches('#').to_owned()
}

pub(crate) fn project_catalog_entry(
    document: &SourceDocument,
    toml: &ParsedToml,
    catalog_id: &str,
    key: &str,
) -> CatalogEntryNode {
    let root = toml.root();
    CatalogEntryNode {
        catalog_id: catalog_id.to_owned(),
        key: key.to_owned(),
        location: table_location(document, root),
        value: json_from_toml_value(root),
    }
}