rscamper 0.2.2

Rust interface to scamper network measurement tool
Documentation
// rscamper - Rust wrapper for scamper list/cycle metadata
//
// 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 std::ffi::CStr;
use std::fmt;
use std::time::{Duration, UNIX_EPOCH, SystemTime};

use crate::ffi::scamper_list::{self, ScamperListT, ScamperCycleT};

/// Represents metadata for a scamper measurement list.
pub struct ScamperList {
    inner: *mut ScamperListT,
}

impl ScamperList {
    pub(crate) unsafe fn from_ptr(ptr: *mut ScamperListT) -> Option<Self> {
        if ptr.is_null() {
            return None;
        }
        let ptr = unsafe { scamper_list::scamper_list_use(ptr) };
        Some(ScamperList { inner: ptr })
    }

    /// Return the list ID.
    pub fn id(&self) -> u32 {
        unsafe { scamper_list::scamper_list_id_get(self.inner) }
    }

    /// Return the list name.
    pub fn name(&self) -> Option<&str> {
        let ptr = unsafe { scamper_list::scamper_list_name_get(self.inner) };
        if ptr.is_null() {
            None
        } else {
            Some(unsafe { CStr::from_ptr(ptr) }.to_str().unwrap_or(""))
        }
    }

    /// Return the list description.
    pub fn descr(&self) -> Option<&str> {
        let ptr = unsafe { scamper_list::scamper_list_descr_get(self.inner) };
        if ptr.is_null() {
            None
        } else {
            Some(unsafe { CStr::from_ptr(ptr) }.to_str().unwrap_or(""))
        }
    }

    /// Return the monitor string.
    pub fn monitor(&self) -> Option<&str> {
        let ptr = unsafe { scamper_list::scamper_list_monitor_get(self.inner) };
        if ptr.is_null() {
            None
        } else {
            Some(unsafe { CStr::from_ptr(ptr) }.to_str().unwrap_or(""))
        }
    }
}

impl Drop for ScamperList {
    fn drop(&mut self) {
        unsafe { scamper_list::scamper_list_free(self.inner) };
    }
}

impl Clone for ScamperList {
    fn clone(&self) -> Self {
        let ptr = unsafe { scamper_list::scamper_list_use(self.inner) };
        ScamperList { inner: ptr }
    }
}

impl PartialEq for ScamperList {
    fn eq(&self, other: &Self) -> bool {
        unsafe { scamper_list::scamper_list_cmp(self.inner, other.inner) == 0 }
    }
}

impl Eq for ScamperList {}

impl fmt::Debug for ScamperList {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("ScamperList")
            .field("id", &self.id())
            .field("name", &self.name())
            .finish()
    }
}

unsafe impl Send for ScamperList {}
unsafe impl Sync for ScamperList {}

/// Represents a measurement cycle within a list.
pub struct ScamperCycle {
    inner: *mut ScamperCycleT,
}

impl ScamperCycle {
    pub(crate) unsafe fn from_ptr(ptr: *mut ScamperCycleT) -> Option<Self> {
        if ptr.is_null() {
            return None;
        }
        let ptr = unsafe { scamper_list::scamper_cycle_use(ptr) };
        Some(ScamperCycle { inner: ptr })
    }

    pub(crate) fn as_ptr(&self) -> *mut ScamperCycleT {
        self.inner
    }

    /// Return the cycle ID.
    pub fn id(&self) -> u32 {
        unsafe { scamper_list::scamper_cycle_id_get(self.inner) }
    }

    /// Return the cycle start time.
    pub fn start(&self) -> SystemTime {
        let t = unsafe { scamper_list::scamper_cycle_start_time_get(self.inner) };
        UNIX_EPOCH + Duration::from_secs(t as u64)
    }

    /// Return the cycle stop time.
    pub fn stop(&self) -> SystemTime {
        let t = unsafe { scamper_list::scamper_cycle_stop_time_get(self.inner) };
        UNIX_EPOCH + Duration::from_secs(t as u64)
    }

    /// Return the hostname.
    pub fn hostname(&self) -> Option<&str> {
        let ptr = unsafe { scamper_list::scamper_cycle_hostname_get(self.inner) };
        if ptr.is_null() {
            None
        } else {
            Some(unsafe { CStr::from_ptr(ptr) }.to_str().unwrap_or(""))
        }
    }

    /// Return the list associated with this cycle.
    pub fn list(&self) -> Option<ScamperList> {
        let ptr = unsafe { scamper_list::scamper_cycle_list_get(self.inner) };
        unsafe { ScamperList::from_ptr(ptr) }
    }
}

impl Drop for ScamperCycle {
    fn drop(&mut self) {
        unsafe { scamper_list::scamper_cycle_free(self.inner) };
    }
}

impl Clone for ScamperCycle {
    fn clone(&self) -> Self {
        let ptr = unsafe { scamper_list::scamper_cycle_use(self.inner) };
        ScamperCycle { inner: ptr }
    }
}

impl fmt::Debug for ScamperCycle {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("ScamperCycle")
            .field("id", &self.id())
            .finish()
    }
}

unsafe impl Send for ScamperCycle {}
unsafe impl Sync for ScamperCycle {}