weavatrix-rust 2.1.1

Protocol-independent Rust repository intelligence: typed evidence graphs for impact, architecture, APIs, Git, search, and memory
Documentation
use super::{
    Bindings, Call, Certainty, Detection, Entity, ProviderHints, Role, Transport, first_value,
    non_empty_resources, positional_values, property, resource_values,
};

pub(super) fn detect_amqp(
    call: &Call<'_, '_>,
    name: &str,
    chain: &str,
    hints: ProviderHints,
    bindings: &Bindings,
) -> Option<Detection> {
    if !is_amqp_call(name, chain, hints) {
        return None;
    }
    let positional = |index| positional_values(call, index, bindings);
    let detection = match name {
        "assertqueue" | "queuedeclare" | "queue_declare" | "createqueue" => Detection {
            transport: Transport::Amqp,
            entity: Entity::Queue,
            role: Role::Declare,
            resources: non_empty_resources(resource_values(call, &["queue", "name"], 0, bindings)),
            exchange: None,
            routing_key: None,
            consumer_group: None,
            certainty: Certainty::Exact,
            uncertainty: None,
        },
        "assertexchange" | "exchangedeclare" | "exchange_declare" => Detection {
            transport: Transport::Amqp,
            entity: Entity::Exchange,
            role: Role::Declare,
            resources: non_empty_resources(resource_values(
                call,
                &["exchange", "name"],
                0,
                bindings,
            )),
            exchange: None,
            routing_key: None,
            consumer_group: None,
            certainty: Certainty::Exact,
            uncertainty: None,
        },
        "bindqueue" | "queuebind" | "queue_bind" => binding_detection(call, bindings),
        "sendtoqueue" | "send_to_queue" => Detection {
            transport: Transport::Amqp,
            entity: Entity::Queue,
            role: Role::Producer,
            resources: non_empty_resources(positional(0)),
            exchange: None,
            routing_key: None,
            consumer_group: None,
            certainty: Certainty::Exact,
            uncertainty: None,
        },
        "consume" | "basicconsume" | "basic_consume" | "rabbitlistener" => Detection {
            transport: Transport::Amqp,
            entity: Entity::Queue,
            role: Role::Consumer,
            resources: non_empty_resources(resource_values(
                call,
                &["queue", "queues"],
                0,
                bindings,
            )),
            exchange: None,
            routing_key: None,
            consumer_group: property(call, &["consumertag", "consumer_tag"]),
            certainty: Certainty::Exact,
            uncertainty: None,
        },
        "publish" | "basicpublish" | "basic_publish" | "publishwithcontext" | "convertandsend" => {
            let exchange = first_value(call, &["exchange"], 0, bindings);
            let routing_key = first_value(call, &["routingkey", "routing_key"], 1, bindings);
            Detection {
                transport: Transport::Amqp,
                entity: Entity::Exchange,
                role: Role::Producer,
                resources: vec![exchange.clone()],
                exchange,
                routing_key,
                consumer_group: None,
                certainty: Certainty::Exact,
                uncertainty: None,
            }
        }
        _ => return None,
    };
    Some(detection)
}

fn is_amqp_call(name: &str, chain: &str, hints: ProviderHints) -> bool {
    const EXPLICIT_CALLS: &[&str] = &[
        "assertqueue",
        "queuedeclare",
        "queue_declare",
        "assertexchange",
        "exchangedeclare",
        "exchange_declare",
        "bindqueue",
        "queuebind",
        "queue_bind",
        "sendtoqueue",
        "basicpublish",
        "basic_publish",
        "basicconsume",
        "basic_consume",
        "rabbitlistener",
    ];
    hints.amqp || hints.rabbitmq || EXPLICIT_CALLS.contains(&name) || chain.contains("amqp")
}

fn binding_detection(call: &Call<'_, '_>, bindings: &Bindings) -> Detection {
    let queue = first_value(call, &["queue"], 0, bindings);
    let go_order = call.name == "QueueBind";
    let exchange = first_value(call, &["exchange"], if go_order { 2 } else { 1 }, bindings);
    let routing_key = first_value(
        call,
        &["routingkey", "routing_key", "bindingkey"],
        if go_order { 1 } else { 2 },
        bindings,
    );
    Detection {
        transport: Transport::Amqp,
        entity: Entity::Binding,
        role: Role::Bind,
        resources: vec![queue],
        exchange,
        routing_key,
        consumer_group: None,
        certainty: Certainty::Exact,
        uncertainty: None,
    }
}