cstats-core 0.1.1

Core library for cstats - statistical analysis and metrics collection
Documentation
//! Statistics collection and analysis

use std::collections::HashMap;
use std::time::{Duration, Instant};

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};

use crate::api::MetricValue;

pub mod aggregator;
pub mod collector;

pub use aggregator::StatsAggregator;
pub use collector::StatsCollector;

/// Statistics record containing collected metrics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StatsRecord {
    /// Unique identifier for this record
    pub id: String,

    /// When the statistics were collected
    pub timestamp: DateTime<Utc>,

    /// Source identifier
    pub source: String,

    /// Collected metrics
    pub metrics: HashMap<String, MetricValue>,

    /// Duration of the measured operation
    pub duration: Option<Duration>,

    /// Additional metadata
    pub metadata: HashMap<String, String>,
}

/// Timer for measuring execution time
#[derive(Debug)]
pub struct Timer {
    start: Instant,
    name: String,
}

impl Timer {
    /// Start a new timer with the given name
    pub fn start(name: impl Into<String>) -> Self {
        Self {
            start: Instant::now(),
            name: name.into(),
        }
    }

    /// Stop the timer and return the elapsed duration
    pub fn stop(self) -> (String, Duration) {
        (self.name, self.start.elapsed())
    }

    /// Get the elapsed time without stopping the timer
    pub fn elapsed(&self) -> Duration {
        self.start.elapsed()
    }

    /// Get the timer name
    pub fn name(&self) -> &str {
        &self.name
    }
}

/// Memory usage statistics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryStats {
    /// Peak memory usage in bytes
    pub peak_bytes: u64,

    /// Current memory usage in bytes
    pub current_bytes: u64,

    /// Number of allocations
    pub allocations: u64,

    /// Number of deallocations
    pub deallocations: u64,
}

/// CPU usage statistics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CpuStats {
    /// CPU usage percentage
    pub usage_percent: f64,

    /// User time in milliseconds
    pub user_time_ms: u64,

    /// System time in milliseconds
    pub system_time_ms: u64,
}

/// System statistics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SystemStats {
    /// Memory statistics
    pub memory: Option<MemoryStats>,

    /// CPU statistics
    pub cpu: Option<CpuStats>,

    /// Load averages
    pub load_avg: Option<[f64; 3]>,

    /// Uptime in seconds
    pub uptime_seconds: Option<u64>,
}

/// Statistics summary for aggregated data
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StatsSummary {
    /// Number of records processed
    pub count: u64,

    /// Time range of the summary
    pub time_range: (DateTime<Utc>, DateTime<Utc>),

    /// Aggregated metrics
    pub metrics: HashMap<String, MetricSummary>,

    /// Sources included in the summary
    pub sources: Vec<String>,
}

/// Summary statistics for a specific metric
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MetricSummary {
    /// Metric name
    pub name: String,

    /// Minimum value
    pub min: f64,

    /// Maximum value
    pub max: f64,

    /// Average value
    pub avg: f64,

    /// Sum of all values
    pub sum: f64,

    /// Number of data points
    pub count: u64,

    /// Standard deviation
    pub std_dev: Option<f64>,

    /// Percentiles (50th, 90th, 95th, 99th)
    pub percentiles: Option<[f64; 4]>,
}

impl StatsRecord {
    /// Create a new statistics record
    pub fn new(source: impl Into<String>) -> Self {
        Self {
            id: uuid::Uuid::new_v4().to_string(),
            timestamp: Utc::now(),
            source: source.into(),
            metrics: HashMap::new(),
            duration: None,
            metadata: HashMap::new(),
        }
    }

    /// Add a metric to the record
    pub fn add_metric(&mut self, name: impl Into<String>, value: MetricValue) {
        self.metrics.insert(name.into(), value);
    }

    /// Set the duration for the record
    pub fn set_duration(&mut self, duration: Duration) {
        self.duration = Some(duration);
    }

    /// Add metadata to the record
    pub fn add_metadata(&mut self, key: impl Into<String>, value: impl Into<String>) {
        self.metadata.insert(key.into(), value.into());
    }
}

/// Utility functions for statistics calculations
pub mod utils {
    /// Calculate percentile from a sorted vector of values
    pub fn percentile(sorted_values: &[f64], p: f64) -> f64 {
        if sorted_values.is_empty() {
            return 0.0;
        }

        let index = (p / 100.0) * (sorted_values.len() - 1) as f64;
        let lower = index.floor() as usize;
        let upper = index.ceil() as usize;

        if lower == upper {
            sorted_values[lower]
        } else {
            let weight = index - lower as f64;
            sorted_values[lower] * (1.0 - weight) + sorted_values[upper] * weight
        }
    }

    /// Calculate standard deviation
    pub fn std_deviation(values: &[f64]) -> f64 {
        if values.len() <= 1 {
            return 0.0;
        }

        let mean = values.iter().sum::<f64>() / values.len() as f64;
        let variance =
            values.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / (values.len() - 1) as f64;

        variance.sqrt()
    }
}