weavatrix-rust 2.14.3

Protocol-independent Rust repository intelligence: typed evidence graphs for impact, architecture, APIs, Git, search, and memory
Documentation
use super::resolve::binding_row;
use super::view::{domain_name, owned_domains};
use crate::engine::RepositoryState;
use blazingly_json::{Value, json};
use weavatrix_graph::Node;

pub(in crate::operations) fn affected(
    state: &RepositoryState,
    files: &[String],
    impacts: &[Value],
) -> Vec<Value> {
    let mut rows = Vec::new();
    for node in state
        .graph()
        .nodes()
        .iter()
        .filter(|node| node.kind.as_str() == "mermaid.diagram")
    {
        if file_changed(node, files) {
            rows.push(json!({
                "diagram": node.label,
                "element": Value::Null,
                "reason": "diagram source is in the changed files",
                "not_proven": ["runtime equivalence of the drawn graph"]
            }));
        }
    }
    for node in state
        .graph()
        .nodes()
        .iter()
        .filter(|node| node.kind.as_str() == "mermaid.binding")
    {
        let binding = binding_row(state, node);
        if binding["status"] != "exact" {
            continue;
        }
        let hit = binding["targets"]
            .as_array()
            .into_iter()
            .flatten()
            .any(|target| file_label_changed(target, files) || impact_hit(target, impacts));
        if hit {
            rows.push(json!({
                "diagram": binding["diagram"],
                "element": binding["element"],
                "reason": "explicit sidecar binding to a changed symbol",
                "check": neighbors(state, binding["element"].as_str()),
                "not_proven": ["the drawn arrow is implemented"]
            }));
        }
    }
    rows
}

fn file_changed(node: &Node, files: &[String]) -> bool {
    node.span.as_ref().is_some_and(|span| {
        files
            .iter()
            .any(|file| span.file == *file || span.file.ends_with(file))
    })
}

fn file_label_changed(target: &Value, files: &[String]) -> bool {
    target["span"]["file"].as_str().is_some_and(|path| {
        files
            .iter()
            .any(|file| path == file || path.ends_with(file))
    })
}

fn impact_hit(target: &Value, impacts: &[Value]) -> bool {
    let id = target["id"].as_str();
    let label = target["label"].as_str();
    impacts
        .iter()
        .any(|item| item["id"].as_str() == id || item["label"].as_str() == label)
}

fn neighbors(state: &RepositoryState, element: Option<&str>) -> Vec<String> {
    let Some(element) = element else {
        return Vec::new();
    };
    let Some(node) = state
        .graph()
        .nodes()
        .iter()
        .find(|node| node.kind.as_str() == "mermaid.element" && node.label == element)
    else {
        return Vec::new();
    };
    owned_domains(state, node.id.as_str())
        .iter()
        .filter_map(|item| domain_name(std::slice::from_ref(item), "label:"))
        .collect()
}