freeswitch-log-parser 0.17.1

Parser for FreeSWITCH log files — handles compressed .xz files, multi-line dumps, truncated buffers, and stateful UUID/timestamp tracking
Documentation
//! mod_loopback A/B leg pairing.
//!
//! The two halves of a loopback call are separate sessions with no peer UUID
//! between them until a channel dump exposes `other_loopback_leg_uuid`. Their
//! names carry the pairing: mod_loopback derives the B leg's name from the A
//! leg's destination number, differing only in the trailing leg letter.

use freeswitch_types::variables::{LoopbackChannelName, LoopbackLeg};

/// The endpoint prefix a loopback channel name carries. `freeswitch-types`
/// parses one but spells no sibling name, so this is the second copy.
const PREFIX: &str = "loopback/";

/// A leg name of the loopback whose B leg is `channel_name`.
pub(crate) fn a_leg_name(channel_name: &str) -> Option<String> {
    let name = LoopbackChannelName::parse(channel_name)?;
    if name.leg() != LoopbackLeg::B {
        return None;
    }
    Some(format!("{PREFIX}{}-a", name.extension()))
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn b_leg_names_its_a_leg() {
        assert_eq!(
            a_leg_name("loopback/tty-b").as_deref(),
            Some("loopback/tty-a")
        );
        assert_eq!(
            a_leg_name("loopback/9-1-1-b").as_deref(),
            Some("loopback/9-1-1-a")
        );
    }

    #[test]
    fn a_leg_and_foreign_endpoints_pair_with_nothing() {
        assert!(a_leg_name("loopback/tty-a").is_none());
        assert!(a_leg_name("sofia/internal/1000-b").is_none());
        assert!(a_leg_name("loopback/-b").is_none());
    }
}