weavatrix-rust 2.2.1

Protocol-independent Rust repository intelligence: typed evidence graphs for impact, architecture, APIs, Git, search, and memory
Documentation
use super::model::ContractResults;
use crate::engine::RepositoryState;
use crate::operations::optional_str;
use crate::operations::transport_contracts::{
    empty_typed_contracts, event_contracts, http_contracts, typed_api_contracts,
};
use blazingly_json::{Value, json};

pub(super) fn analyze(
    backend: &RepositoryState,
    clients: &[(String, &RepositoryState)],
    args: &Value,
) -> Result<ContractResults, String> {
    let transport = optional_str(args, "transport")?.unwrap_or("all");
    if !matches!(transport, "all" | "http" | "graphql" | "grpc" | "event") {
        return Err("transport must be all, http, graphql, grpc, or event".to_owned());
    }
    if args.get("runtime_config").is_some() {
        return Err(
            "runtime_config is not revision-bound evidence; use runtime_evidence_files".to_owned(),
        );
    }
    let http = if matches!(transport, "all" | "http") {
        http_contracts(backend, clients, args)?
    } else {
        empty_contracts(&json!({"endpoints": 0, "matches": 0, "method_mismatches": 0}))
    };
    let events = if matches!(transport, "all" | "event") {
        event_contracts(backend, clients, args)?
    } else {
        empty_contracts(&json!({"contracts": 0, "matches": 0}))
    };
    let graphql = typed_contract(transport, "graphql", backend, clients, args)?;
    let grpc = typed_contract(transport, "grpc", backend, clients, args)?;
    Ok(ContractResults {
        transport: transport.to_owned(),
        http,
        events,
        graphql,
        grpc,
    })
}

fn empty_contracts(totals: &Value) -> Value {
    json!({
        "status": "NOT_APPLICABLE",
        "totals": totals,
        "contracts": []
    })
}

fn typed_contract(
    requested: &str,
    transport: &str,
    backend: &RepositoryState,
    clients: &[(String, &RepositoryState)],
    args: &Value,
) -> Result<Value, String> {
    if matches!(requested, "all") || requested == transport {
        typed_api_contracts(backend, clients, args, transport)
    } else {
        Ok(empty_typed_contracts(transport))
    }
}

pub(super) fn completeness_reasons(results: &ContractResults) -> Vec<String> {
    [&results.events, &results.graphql, &results.grpc]
        .into_iter()
        .flat_map(|result| {
            result
                .pointer("/completeness/reasons")
                .and_then(Value::as_array)
                .into_iter()
                .flatten()
        })
        .filter_map(Value::as_str)
        .map(str::to_owned)
        .collect()
}

pub(super) fn evidence(results: &ContractResults) -> Vec<Value> {
    let mut evidence = contracts(&results.http)
        .chain(contracts(&results.events))
        .chain(contracts(&results.graphql))
        .chain(contracts(&results.grpc))
        .cloned()
        .collect::<Vec<_>>();
    evidence.sort_by(|left, right| {
        left["key"]
            .as_str()
            .unwrap_or_default()
            .cmp(right["key"].as_str().unwrap_or_default())
    });
    evidence
}

pub(super) fn matches(results: &ContractResults) -> Vec<Value> {
    contracts(&results.http)
        .chain(array(&results.events, "matches"))
        .chain(array(&results.graphql, "matches"))
        .chain(array(&results.grpc, "matches"))
        .cloned()
        .collect()
}

pub(super) fn unmatched_endpoints(results: &ContractResults) -> u64 {
    [&results.http, &results.graphql, &results.grpc]
        .into_iter()
        .map(|result| {
            result
                .pointer("/totals/unmatched_endpoints")
                .and_then(Value::as_u64)
                .unwrap_or(0)
        })
        .sum()
}

fn contracts(value: &Value) -> impl Iterator<Item = &Value> {
    array(value, "contracts")
}

fn array<'value>(value: &'value Value, key: &str) -> impl Iterator<Item = &'value Value> {
    value[key].as_array().into_iter().flatten()
}