dc-runner-cli 0.2.12

Required Rust runner lane for Data Contracts.
use std::collections::HashMap;

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ResolvedServiceImplementation {
    pub implementation_id: String,
    pub service_type: String,
    pub profile: String,
    pub imports: Vec<String>,
    pub packaging_mode: String,
}

fn key(service_type: &str, profile: &str) -> (String, String) {
    (service_type.to_string(), profile.to_string())
}

pub fn builtin_registry() -> HashMap<(String, String), ResolvedServiceImplementation> {
    let mut out = HashMap::new();
    out.insert(
        key("io.http", "request.http"),
        ResolvedServiceImplementation {
            implementation_id: "builtin.io.http.v1".to_string(),
            service_type: "io.http".to_string(),
            profile: "request.http".to_string(),
            imports: vec![
                "pipe_identity".to_string(),
                "assert_truth".to_string(),
                "get_json".to_string(),
                "post_json".to_string(),
            ],
            packaging_mode: "builtin".to_string(),
        },
    );
    out.insert(
        key("io.fs", "read.text"),
        ResolvedServiceImplementation {
            implementation_id: "builtin.io.fs.v1".to_string(),
            service_type: "io.fs".to_string(),
            profile: "read.text".to_string(),
            imports: vec![
                "pipe_identity".to_string(),
                "assert_truth".to_string(),
                "read_text".to_string(),
                "write_text".to_string(),
            ],
            packaging_mode: "builtin".to_string(),
        },
    );
    out.insert(
        key("io.system", "exec.command"),
        ResolvedServiceImplementation {
            implementation_id: "builtin.io.system.v1".to_string(),
            service_type: "io.system".to_string(),
            profile: "exec.command".to_string(),
            imports: vec![
                "pipe_identity".to_string(),
                "assert_truth".to_string(),
                "exec".to_string(),
                "now".to_string(),
            ],
            packaging_mode: "builtin".to_string(),
        },
    );
    out.insert(
        key("io.mysql", "mysql.query"),
        ResolvedServiceImplementation {
            implementation_id: "builtin.io.mysql.v1".to_string(),
            service_type: "io.mysql".to_string(),
            profile: "mysql.query".to_string(),
            imports: vec!["query".to_string(), "migrate".to_string()],
            packaging_mode: "builtin".to_string(),
        },
    );
    out.insert(
        key("io.docs", "generate.docs"),
        ResolvedServiceImplementation {
            implementation_id: "builtin.io.docs.v1".to_string(),
            service_type: "io.docs".to_string(),
            profile: "generate.docs".to_string(),
            imports: vec![
                "pipe_identity".to_string(),
                "assert_truth".to_string(),
                "render_docs".to_string(),
            ],
            packaging_mode: "builtin".to_string(),
        },
    );
    out
}

pub fn resolve_exact(
    service_type: &str,
    profile: &str,
) -> Result<ResolvedServiceImplementation, String> {
    let reg = builtin_registry();
    reg.get(&(service_type.to_string(), profile.to_string()))
        .cloned()
        .ok_or_else(|| {
            format!("runtime.services.unresolved_type_profile: {service_type}/{profile}")
        })
}

pub fn validate_imports_subset(
    impl_ref: &ResolvedServiceImplementation,
    declared_imports: &[String],
) -> Result<(), String> {
    for name in declared_imports {
        if !impl_ref.imports.iter().any(|allowed| allowed == name) {
            return Err(format!(
                "runtime.services.import_not_supported: {}:{}:{}",
                impl_ref.service_type, impl_ref.profile, name
            ));
        }
    }
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn resolve_exact_known_profile() {
        let resolved = resolve_exact("io.http", "request.http").expect("expected resolve");
        assert_eq!(resolved.implementation_id, "builtin.io.http.v1");
    }

    #[test]
    fn resolve_exact_unknown_profile_fails() {
        let err = resolve_exact("io.http", "unknown.profile").expect_err("expected fail");
        assert!(err.contains("runtime.services.unresolved_type_profile"));
    }
}