weavatrix-rust 2.14.3

Protocol-independent Rust repository intelligence: typed evidence graphs for impact, architecture, APIs, Git, search, and memory
Documentation
mod docs;
mod resolve;
mod view;
mod walk;

use crate::engine::RepositoryState;
use crate::operations::{arg_str, optional_str, optional_u64, reject_unknown_arguments};
use blazingly_json::{Value, json};
use weavatrix_graph::Node;

pub(in crate::operations) use docs::affected;

pub(super) fn inventory(state: &RepositoryState, args: &Value) -> Result<Value, String> {
    reject_unknown_arguments("diagram_inventory", args, &["path", "max_results"])?;
    let path = optional_str(args, "path")?;
    let max = usize::try_from(optional_u64(args, "max_results")?.unwrap_or(200))
        .map_err(|_| "max_results is too large")?;
    if max == 0 || max > 500 {
        return Err("max_results must be between 1 and 500".to_owned());
    }
    let all = state
        .graph()
        .nodes()
        .iter()
        .filter(|node| node.kind.as_str() == "mermaid.diagram")
        .filter(|node| path.is_none_or(|filter| view::in_path(node, filter)))
        .collect::<Vec<_>>();
    let found = all.len();
    let diagrams = all
        .into_iter()
        .take(max)
        .map(|node| view::diagram_row(state, node))
        .collect::<Vec<_>>();
    let decode_cut = state
        .snapshot()
        .diagnostics
        .iter()
        .any(|item| item.code == "mermaid.limit" || item.code == "mermaid.unsupported");
    let page_cut = found > diagrams.len();
    let mut reasons = Vec::new();
    if page_cut {
        reasons.push("max_results");
    }
    if decode_cut {
        reasons.push("decode");
    }
    Ok(json!({
        "diagrams": diagrams,
        "bindings": bindings(state, path, max),
        "bounds": {
            "truncated": page_cut || decode_cut,
            "found": found,
            "shown": diagrams.len(),
            "reasons": reasons,
            "revision": state.snapshot().revision,
            "runtime": false
        }
    }))
}

pub(super) fn trace(state: &RepositoryState, args: &Value) -> Result<Value, String> {
    walk::trace(state, args)
}

pub(super) fn context(state: &RepositoryState, args: &Value) -> Result<Value, String> {
    reject_unknown_arguments("diagram_context", args, &["label", "task", "max_related"])?;
    let label = arg_str(args, "label")?;
    let task = optional_str(args, "task")?.unwrap_or("inspect");
    let max_related = usize::try_from(optional_u64(args, "max_related")?.unwrap_or(24))
        .map_err(|_| "max_related is too large")?;
    if max_related == 0 || max_related > 200 {
        return Err("max_related must be between 1 and 200".to_owned());
    }
    let index = state.resolve_node(label)?;
    let node = state.node(index)?;
    let domains = view::owned_domains(state, node.id.as_str());
    let relations = declared_neighbors(state, node, max_related);
    let selected_bindings = bindings_for(state, node);
    let fragments = fragments(state, node);
    let gaps = gaps(task, &selected_bindings);
    Ok(json!({
        "selected": {
            "id": node.id,
            "label": node.label,
            "kind": node.kind,
            "span": node.span,
            "display": view::domain_name(&domains, "label:")
        },
        "task": task,
        "relations": relations,
        "bindings": selected_bindings,
        "fragments": fragments,
        "gaps": gaps,
        "bounds": {
            "truncated": false,
            "revision": state.snapshot().revision,
            "complete": gaps.is_empty(),
            "runtime": false,
            "plane": "declared_architecture"
        }
    }))
}

fn bindings(state: &RepositoryState, path: Option<&str>, max: usize) -> Vec<Value> {
    state
        .graph()
        .nodes()
        .iter()
        .filter(|node| node.kind.as_str() == "mermaid.binding")
        .filter(|node| path.is_none_or(|filter| view::in_path(node, filter)))
        .take(max)
        .map(|node| resolve::binding_row(state, node))
        .collect()
}

fn bindings_for(state: &RepositoryState, node: &Node) -> Vec<Value> {
    bindings(state, None, 24)
        .into_iter()
        .filter(|item| {
            item["element"].as_str() == Some(node.label.as_str())
                || item["diagram"].as_str() == Some(node.label.as_str())
        })
        .collect()
}

fn declared_neighbors(state: &RepositoryState, node: &Node, max: usize) -> Vec<Value> {
    state
        .graph()
        .edges()
        .iter()
        .filter(|edge| {
            edge.kind.as_str() == "declared_architecture"
                && (edge.source.as_str() == node.id.as_str()
                    || edge.target.as_str() == node.id.as_str())
        })
        .filter_map(|edge| {
            let other = if edge.source.as_str() == node.id.as_str() {
                state.graph().node(edge.target.as_str())?
            } else {
                state.graph().node(edge.source.as_str())?
            };
            Some(json!({
                "id": other.id,
                "label": other.label,
                "relation": edge.kind,
                "detail": edge.provenance.detail,
                "span": other.span
            }))
        })
        .take(max)
        .collect()
}

fn fragments(state: &RepositoryState, node: &Node) -> Vec<Value> {
    if node.span.is_none() {
        return Vec::new();
    }
    let Ok(source) = crate::operations::source::read_source(
        state,
        &json!({"label": node.id.as_str(), "before": 1, "after": 12}),
    ) else {
        return Vec::new();
    };
    vec![json!({
        "path": source["path"],
        "start_line": source["start_line"],
        "lines": source["lines"]
    })]
}

fn gaps(task: &str, bindings: &[Value]) -> Vec<String> {
    let mut gaps = vec!["drawn arrows are not production Calls".to_owned()];
    if !task.is_empty() && bindings.iter().all(|item| item["status"] != "exact") {
        gaps.push("no exact implementation binding for this element".to_owned());
    }
    if bindings.iter().any(|item| item["status"] == "ambiguous") {
        gaps.push("binding is ambiguous".to_owned());
    }
    gaps
}