rscamper 0.2.2

Rust interface to scamper network measurement tool
Documentation
// rscamper - Rust wrapper for ICMP extensions
//
// Copyright (C) 2026 Dimitrios Giakatos
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 3.

use crate::ffi::scamper_icmpext::{self, ScamperIcmpextT, ScamperIcmpextsT};

/// A single ICMP extension entry (e.g. an MPLS label stack).
pub struct ScamperIcmpExt {
    inner: *mut ScamperIcmpextT,
}

impl ScamperIcmpExt {
    pub(crate) unsafe fn from_ptr(ptr: *mut ScamperIcmpextT) -> Option<Self> {
        if ptr.is_null() {
            return None;
        }
        let ptr = unsafe { scamper_icmpext::scamper_icmpext_use(ptr) };
        Some(ScamperIcmpExt { inner: ptr })
    }

    /// Return true if this extension is an MPLS label stack.
    pub fn is_mpls(&self) -> bool {
        unsafe { scamper_icmpext::scamper_icmpext_is_mpls(self.inner) != 0 }
    }

    /// Return the number of MPLS labels.
    pub fn mpls_count(&self) -> u16 {
        unsafe { scamper_icmpext::scamper_icmpext_mpls_count_get(self.inner) }
    }

    /// Return the MPLS label at index `i`.
    pub fn mpls_label(&self, i: u16) -> u32 {
        unsafe { scamper_icmpext::scamper_icmpext_mpls_label_get(self.inner, i) }
    }

    /// Return the MPLS TTL at index `i`.
    pub fn mpls_ttl(&self, i: u16) -> u8 {
        unsafe { scamper_icmpext::scamper_icmpext_mpls_ttl_get(self.inner, i) }
    }

    /// Return the MPLS experimental bits at index `i`.
    pub fn mpls_exp(&self, i: u16) -> u8 {
        unsafe { scamper_icmpext::scamper_icmpext_mpls_exp_get(self.inner, i) }
    }

    /// Return the MPLS bottom-of-stack bit at index `i`.
    pub fn mpls_s(&self, i: u16) -> u8 {
        unsafe { scamper_icmpext::scamper_icmpext_mpls_s_get(self.inner, i) }
    }

    /// Return an iterator over MPLS label stack entries.
    pub fn mpls_labels(&self) -> MplsIter<'_> {
        MplsIter { ext: self, index: 0, count: self.mpls_count() }
    }
}

impl Drop for ScamperIcmpExt {
    fn drop(&mut self) {
        unsafe { scamper_icmpext::scamper_icmpext_free(self.inner) };
    }
}

impl Clone for ScamperIcmpExt {
    fn clone(&self) -> Self {
        let ptr = unsafe { scamper_icmpext::scamper_icmpext_use(self.inner) };
        ScamperIcmpExt { inner: ptr }
    }
}

unsafe impl Send for ScamperIcmpExt {}
unsafe impl Sync for ScamperIcmpExt {}

/// An MPLS label stack entry.
#[derive(Debug, Clone, Copy)]
pub struct MplsLabel {
    pub label: u32,
    pub ttl: u8,
    pub exp: u8,
    pub s: u8,
}

/// Iterator over MPLS labels in a ScamperIcmpExt.
pub struct MplsIter<'a> {
    ext: &'a ScamperIcmpExt,
    index: u16,
    count: u16,
}

impl<'a> Iterator for MplsIter<'a> {
    type Item = MplsLabel;
    fn next(&mut self) -> Option<Self::Item> {
        if self.index >= self.count {
            return None;
        }
        let i = self.index;
        self.index += 1;
        Some(MplsLabel {
            label: self.ext.mpls_label(i),
            ttl:   self.ext.mpls_ttl(i),
            exp:   self.ext.mpls_exp(i),
            s:     self.ext.mpls_s(i),
        })
    }
}

/// A container of ICMP extensions.
pub struct ScamperIcmpExts {
    inner: *mut ScamperIcmpextsT,
}

impl ScamperIcmpExts {
    pub(crate) unsafe fn from_ptr(ptr: *mut ScamperIcmpextsT) -> Option<Self> {
        if ptr.is_null() {
            return None;
        }
        let ptr = unsafe { scamper_icmpext::scamper_icmpexts_use(ptr) };
        Some(ScamperIcmpExts { inner: ptr })
    }

    /// Return the number of extensions.
    pub fn count(&self) -> u16 {
        unsafe { scamper_icmpext::scamper_icmpexts_count_get(self.inner) }
    }

    /// Return extension at index `i`.
    pub fn get(&self, i: u16) -> Option<ScamperIcmpExt> {
        let ptr = unsafe { scamper_icmpext::scamper_icmpexts_ext_get(self.inner, i) };
        unsafe { ScamperIcmpExt::from_ptr(ptr) }
    }

    /// Iterate over all extensions.
    pub fn iter(&self) -> IcmpExtsIter<'_> {
        IcmpExtsIter { exts: self, index: 0, count: self.count() }
    }
}

impl Drop for ScamperIcmpExts {
    fn drop(&mut self) {
        unsafe { scamper_icmpext::scamper_icmpexts_free(self.inner) };
    }
}

impl Clone for ScamperIcmpExts {
    fn clone(&self) -> Self {
        let ptr = unsafe { scamper_icmpext::scamper_icmpexts_use(self.inner) };
        ScamperIcmpExts { inner: ptr }
    }
}

unsafe impl Send for ScamperIcmpExts {}
unsafe impl Sync for ScamperIcmpExts {}

/// Iterator over ScamperIcmpExts.
pub struct IcmpExtsIter<'a> {
    exts: &'a ScamperIcmpExts,
    index: u16,
    count: u16,
}

impl<'a> Iterator for IcmpExtsIter<'a> {
    type Item = ScamperIcmpExt;
    fn next(&mut self) -> Option<Self::Item> {
        if self.index >= self.count {
            return None;
        }
        let i = self.index;
        self.index += 1;
        self.exts.get(i)
    }
}