netpulse-cli 0.1.1

A zero-config, single-binary network quality monitor with percentile stats, jitter, and MTR-style traceroute
Documentation
// src/state.rs — Shared application state
//
// AppState is the single source of truth shared between:
// - Prober tasks (write: push ProbeResult, update seq/last_rtt)
// - TUI renderer (read: sparkline data, stats, status)
// - Stream exporter (read: stats snapshots)
//
// Wrapped in Arc<Mutex<>> for safe cross-task sharing.
// Lock contention is minimal — probers write once per interval (≥ 100ms),
// TUI reads once per render frame (~100ms).

use crate::stats::ring_buffer::RingBuffer;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};

/// Per-target probe state.
pub struct TargetState {
    /// Original display name (e.g., "google.com" or "8.8.8.8")
    pub display_name: String,
    /// Resolved IP address used for ICMP probing
    pub resolved_addr: String,
    /// Ring buffer of recent probe results
    pub buffer: RingBuffer,
    /// Total probes sent (used as sequence counter)
    pub seq: u64,
    /// RTT of the most recent probe in microseconds; None = loss
    pub last_rtt_us: Option<u64>,
    /// Latest responder IP address (used primarily in trace mode)
    pub last_ip: Option<String>,
}

impl TargetState {
    pub fn new(display_name: &str, resolved_addr: &str, window: usize) -> Self {
        Self {
            display_name: display_name.to_string(),
            resolved_addr: resolved_addr.to_string(),
            buffer: RingBuffer::new(window),
            seq: 0,
            last_rtt_us: None,
            last_ip: None,
        }
    }

    pub fn new_hop(window: usize) -> Self {
        Self {
            display_name: String::new(),
            resolved_addr: String::new(),
            buffer: RingBuffer::new(window),
            seq: 0,
            last_rtt_us: None,
            last_ip: None,
        }
    }
}

/// Shared application state: one entry per monitored target.
pub type AppState = Arc<Mutex<HashMap<String, TargetState>>>;

/// Create a new empty AppState.
pub fn new_app_state() -> AppState {
    Arc::new(Mutex::new(HashMap::new()))
}