weavatrix-rust 2.16.0

Protocol-independent Rust repository intelligence: typed evidence graphs for impact, architecture, APIs, Git, search, and memory
Documentation
use super::abi::{AbiDocument, Completeness, Profile, decode_array};
use super::limits::MAX_SOURCES;
use blazingly_json::Value;

#[must_use]
pub(super) fn extract(
    path: &str,
    raw: &str,
    value: &Value,
    class: super::detect::DocumentClass,
) -> Vec<AbiDocument> {
    match class {
        super::detect::DocumentClass::AbiArray => value
            .as_array()
            .and_then(|items| {
                decode_array(
                    path,
                    raw,
                    items,
                    Profile::AbiJson,
                    None,
                    "strict ABI JSON".into(),
                    Completeness::Full,
                )
            })
            .into_iter()
            .collect(),
        super::detect::DocumentClass::FoundryArtifact => foundry_artifact(path, raw, value),
        super::detect::DocumentClass::SolcOutput => {
            solc_output(path, raw, value, Profile::SolcOutput)
        }
        super::detect::DocumentClass::FoundryBuildInfo => value
            .get("output")
            .map(|output| solc_output(path, raw, output, Profile::FoundryBuildInfo))
            .unwrap_or_default(),
        super::detect::DocumentClass::Hardhat3Incomplete => hardhat3(path, raw, value),
        super::detect::DocumentClass::SolcInput | super::detect::DocumentClass::Other => Vec::new(),
    }
}

fn foundry_artifact(path: &str, raw: &str, value: &Value) -> Vec<AbiDocument> {
    let Some(items) = value.get("abi").and_then(Value::as_array) else {
        return Vec::new();
    };
    decode_array(
        path,
        raw,
        items,
        Profile::FoundryArtifact,
        contract_name(path),
        "decoded abi array from Foundry artifact JSON; source and build binding not verified"
            .into(),
        Completeness::ArtifactOnly,
    )
    .into_iter()
    .collect()
}

fn solc_output(path: &str, raw: &str, value: &Value, profile: Profile) -> Vec<AbiDocument> {
    let Some(contracts) = value.get("contracts").and_then(Value::as_object) else {
        return Vec::new();
    };
    let mut documents = Vec::new();
    let mut seen = 0_usize;
    for (source, named) in contracts {
        let Some(named) = named.as_object() else {
            continue;
        };
        for (name, body) in named {
            seen += 1;
            if seen > MAX_SOURCES {
                return documents;
            }
            let Some(items) = body.get("abi").and_then(Value::as_array) else {
                continue;
            };
            if let Some(document) = decode_array(
                path,
                raw,
                items,
                profile,
                Some(format!("{source}:{name}")),
                "decoded abi array from solc output JSON; source hashes and outputSelection were not verified".into(),
                Completeness::Partial,
            ) {
                documents.push(document);
            }
        }
    }
    documents
}

fn hardhat3(path: &str, raw: &str, value: &Value) -> Vec<AbiDocument> {
    let Some(items) = value.get("abi").and_then(Value::as_array) else {
        return Vec::new();
    };
    decode_array(
        path,
        raw,
        items,
        Profile::Hardhat3Incomplete,
        contract_name(path),
        "Hardhat 3 artifact without a paired output is incomplete".into(),
        Completeness::Partial,
    )
    .into_iter()
    .collect()
}

fn contract_name(path: &str) -> Option<String> {
    std::path::Path::new(path)
        .file_stem()
        .and_then(|name| name.to_str())
        .map(str::to_owned)
}