weavatrix-rust 2.2.1

Protocol-independent Rust repository intelligence: typed evidence graphs for impact, architecture, APIs, Git, search, and memory
Documentation
use super::contracts;
use super::model::{ContractResults, EvidencePage, TraceSummary};
use crate::engine::RepositoryState;
use blazingly_json::{Value, json};
use std::collections::BTreeSet;

pub(super) fn summarize(results: &ContractResults) -> TraceSummary {
    let http_mismatches = total(&results.http, "method_mismatches");
    let event_mismatches = total(&results.events, "mismatches");
    let typed_mismatches =
        total(&results.graphql, "mismatches") + total(&results.grpc, "mismatches");
    let matched = [
        &results.http,
        &results.events,
        &results.graphql,
        &results.grpc,
    ]
    .into_iter()
    .map(|result| total(result, "matches"))
    .sum();
    let verdict = if typed_mismatches > 0 {
        "TYPED_API_CONTRACT_MISMATCH"
    } else if event_mismatches > 0 {
        "EVENT_CONTRACT_MISMATCH"
    } else if http_mismatches > 0 {
        "HTTP_METHOD_MISMATCH"
    } else if matched > 0 {
        "MATCHED"
    } else {
        "NO_STATIC_CLIENT_MATCH"
    };
    TraceSummary {
        verdict,
        http_mismatches,
        event_mismatches,
        typed_mismatches,
        matched,
    }
}

pub(super) fn build(
    backend: &RepositoryState,
    clients: &BTreeSet<&str>,
    results: &ContractResults,
    page: &EvidencePage,
    reasons: &[String],
    summary: &TraceSummary,
) -> Value {
    let has_more = page.end < page.total_items;
    let next_cursor = has_more.then(|| format!("v1:{}", page.end));
    let matched_contracts = contracts::matches(results);
    let unmatched = contracts::unmatched_endpoints(results);
    json!({
        "crossRepoContractV": 1,
        "status": "COMPLETE",
        "verdict": {
            "code": summary.verdict,
            "method_mismatches": summary.http_mismatches,
            "event_mismatches": summary.event_mismatches,
            "typed_contract_mismatches": summary.typed_mismatches,
            "matched_contracts": summary.matched
        },
        "repositories": {
            "backend": backend.root(),
            "clients": clients
        },
        "transport": results.transport,
        "http": results.http,
        "graphql": results.graphql,
        "grpc": results.grpc,
        "transport_contracts": results.events,
        "matches": matched_contracts,
        "unmatched_endpoints": unmatched,
        "evidencePage": {
            "detail": page.detail,
            "offset": page.offset,
            "page_size": page.page_size,
            "total_items": page.total_items,
            "returned_items": page.items.len(),
            "has_more": has_more,
            "next_cursor": next_cursor,
            "items": page.items
        },
        "completeness": {
            "complete": reasons.is_empty(),
            "status": "COMPLETE",
            "reasons": reasons
        },
        "precision": "lossless-parser-derived GraphQL/protobuf contracts, exact typed graph matches, event semantics, and exact or template-prefix HTTP literals",
        "dynamic_contracts": {
            "evaluated": true,
            "method": "bounded static candidates plus revision-bound runtime evidence when supplied"
        },
        "source_mutation": "NONE",
        "network": "NONE"
    })
}

fn total(value: &Value, key: &str) -> u64 {
    value["totals"][key].as_u64().unwrap_or(0)
}