wayle-sysinfo 0.1.2

CPU, memory, disk, and network metrics
Documentation
use std::{collections::HashMap, time::Duration};

use sysinfo::Networks;
use tokio::time::interval;
use tokio_util::sync::CancellationToken;
use tracing::debug;
use wayle_core::Property;

use crate::types::NetworkData;

pub(crate) fn spawn(
    token: CancellationToken,
    network: Property<Vec<NetworkData>>,
    poll_interval: Duration,
) {
    let interval_secs = poll_interval.as_secs_f64();

    tokio::spawn(async move {
        let mut networks = Networks::new_with_refreshed_list();
        let mut ticker = interval(poll_interval);
        let mut prev_rx: HashMap<String, u64> = HashMap::new();
        let mut prev_tx: HashMap<String, u64> = HashMap::new();

        loop {
            if !network.has_subscribers() {
                prev_rx.clear();
                prev_tx.clear();

                tokio::select! {
                    _ = token.cancelled() => {
                        debug!("Network polling cancelled");
                        return;
                    }
                    _ = network.wait_for_subscribers() => {}
                }
                ticker.reset();
            }

            if !network.has_subscribers() {
                continue;
            }

            networks.refresh(true);

            let data: Vec<NetworkData> = networks
                .iter()
                .map(|(name, net)| {
                    let rx = net.total_received();
                    let tx = net.total_transmitted();

                    let last_rx = prev_rx.get(name).copied().unwrap_or(rx);
                    let last_tx = prev_tx.get(name).copied().unwrap_or(tx);

                    let rx_delta = rx.saturating_sub(last_rx);
                    let tx_delta = tx.saturating_sub(last_tx);

                    let rx_per_sec = (rx_delta as f64 / interval_secs) as u64;
                    let tx_per_sec = (tx_delta as f64 / interval_secs) as u64;

                    prev_rx.insert(name.clone(), rx);
                    prev_tx.insert(name.clone(), tx);

                    NetworkData {
                        interface: name.clone(),
                        rx_bytes: rx,
                        tx_bytes: tx,
                        rx_bytes_per_sec: rx_per_sec,
                        tx_bytes_per_sec: tx_per_sec,
                    }
                })
                .collect();

            network.set(data);

            tokio::select! {
                _ = token.cancelled() => {
                    debug!("Network polling cancelled");
                    return;
                }
                _ = ticker.tick() => {}
            }
        }
    });
}