net-mumu 0.2.0-rc.3

Network tools plugin for the Lava language
Documentation
// src/lldp/row.rs
//
// Row shape emitted by net:lldp. Convertible to MuMu Value::KeyedArray.
//
// This struct represents a *snapshot* of a discovered neighbor. Event
// semantics (add/update/remove) are layered on top by the engine, which
// wraps `to_value()` with extra keys:
//   • ok: true
//   • event: "add" | "update" | "remove"
//
// Keys produced by `to_value()` are stable and JSON-friendly.

use indexmap::IndexMap;
use mumu::parser::types::Value;

use super::proto::DiscoveryProtocol;

#[derive(Clone, Debug)]
pub struct LldpRow {
    /// Interface on which the frame was seen.
    pub interface: String,
    /// Protocol that produced the record (LLDP/CDP/...).
    pub protocol: DiscoveryProtocol,
    /// Chassis identifier (MAC, chassis name, etc., protocol-dependent).
    pub chassis_id: String,
    /// Port identifier (ifName, MAC, etc., protocol-dependent).
    pub port_id: String,
    /// Optional TLVs / fields commonly used downstream:
    pub system_name: Option<String>,
    pub system_desc: Option<String>,
    pub port_desc: Option<String>,
    pub vlan: Option<String>,
    pub management_ip: Option<String>,
    pub capabilities: Vec<String>,
    /// Advertised TTL in seconds (if known).
    pub ttl: Option<u16>,
    /// Local timestamp (ms) when this sample was created.
    pub timestamp_ms: u64,
}

impl LldpRow {
    /// Convert into a JSON-friendly keyed map that downstream Flow operators
    /// can encode/print/route easily. The engine will *prepend* event metadata.
    pub fn to_value(&self) -> Value {
        let mut m: IndexMap<String, Value> = IndexMap::new();

        // Stable identity & context
        m.insert("iface".into(), Value::SingleString(self.interface.clone()));
        m.insert(
            "protocol".into(),
            Value::SingleString(self.protocol.as_str().to_string()),
        );
        m.insert("chassis_id".into(), Value::SingleString(self.chassis_id.clone()));
        m.insert("port_id".into(), Value::SingleString(self.port_id.clone()));

        // Optional TLVs / attributes
        if let Some(s) = &self.system_name {
            m.insert("system_name".into(), Value::SingleString(s.clone()));
        }
        if let Some(s) = &self.system_desc {
            m.insert("system_desc".into(), Value::SingleString(s.clone()));
        }
        if let Some(s) = &self.port_desc {
            m.insert("port_desc".into(), Value::SingleString(s.clone()));
        }
        if let Some(v) = &self.vlan {
            m.insert("vlan".into(), Value::SingleString(v.clone()));
        }
        if let Some(ip) = &self.management_ip {
            m.insert("management_ip".into(), Value::SingleString(ip.clone()));
        }
        if !self.capabilities.is_empty() {
            m.insert(
                "capabilities".into(),
                Value::StrArray(self.capabilities.clone()),
            );
        }
        if let Some(ttl) = self.ttl {
            m.insert("ttl".into(), Value::Int(ttl as i32));
        }

        // Always include a timestamp for downstream windowing/joins.
        m.insert("timestamp_ms".into(), Value::Long(self.timestamp_ms as i64));

        Value::KeyedArray(m)
    }
}