ntp-timer 0.1.0

A lightweight NTP time synchronization library for Rust applications. Provides in-process cached global time with background sync and clock-jump detection.
Documentation
use serde::{Deserialize, Serialize};

/// Configuration for NTP timer
///
/// Supports builder pattern for easy customization
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config {
    /// Background synchronization interval in seconds (default: 600)
    pub sync_interval_secs: u64,

    /// Clock jump detection threshold in seconds (default: 5)
    pub clock_jump_threshold_secs: i64,

    /// List of NTP servers to query
    pub ntp_servers: Vec<String>,

    /// Maximum retry attempts (default: 3)
    pub max_retries: usize,

    /// Socket timeout in seconds (default: 5)
    pub socket_timeout_secs: u64,
}

impl Default for Config {
    fn default() -> Self {
        Self {
            sync_interval_secs: 600,
            clock_jump_threshold_secs: 5,
            ntp_servers: vec![
                "ntp.ntsc.ac.cn".to_string(),
                "time1.ntsc.ac.cn".to_string(),
                "time2.ntsc.ac.cn".to_string(),
            ],
            max_retries: 3,
            socket_timeout_secs: 5,
        }
    }
}

impl Config {
    /// Creates a new configuration with default values
    pub fn new() -> Self {
        Self::default()
    }

    /// Sets the background synchronization interval
    pub fn with_sync_interval(mut self, secs: u64) -> Self {
        self.sync_interval_secs = secs;
        self
    }

    /// Sets the clock jump detection threshold
    pub fn with_jump_threshold(mut self, secs: i64) -> Self {
        self.clock_jump_threshold_secs = secs;
        self
    }

    /// 设置 NTP 服务器列表
    pub fn with_ntp_servers(mut self, servers: Vec<String>) -> Self {
        self.ntp_servers = servers;
        self
    }

    /// 设置最大重试次数
    pub fn with_max_retries(mut self, retries: usize) -> Self {
        self.max_retries = retries;
        self
    }

    /// 设置套接字超时
    pub fn with_socket_timeout(mut self, secs: u64) -> Self {
        self.socket_timeout_secs = secs;
        self
    }

    /// 验证配置合法性
    pub fn validate(&self) -> Result<(), String> {
        if self.sync_interval_secs == 0 {
            return Err("同步间隔必须 > 0".to_string());
        }
        if self.clock_jump_threshold_secs < 0 {
            return Err("时钟跳跃阈值必须 >= 0".to_string());
        }
        if self.ntp_servers.is_empty() {
            return Err("NTP 服务器列表不能为空".to_string());
        }
        if self.max_retries == 0 {
            return Err("最大重试次数必须 > 0".to_string());
        }
        if self.socket_timeout_secs == 0 {
            return Err("套接字超时必须 > 0".to_string());
        }
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_default_config() {
        let config = Config::default();
        assert_eq!(config.sync_interval_secs, 600);
        assert_eq!(config.clock_jump_threshold_secs, 5);
        assert_eq!(config.max_retries, 3);
        assert_eq!(config.socket_timeout_secs, 5);
    }

    #[test]
    fn test_builder_pattern() {
        let config = Config::new()
            .with_sync_interval(300)
            .with_jump_threshold(3)
            .with_max_retries(5);

        assert_eq!(config.sync_interval_secs, 300);
        assert_eq!(config.clock_jump_threshold_secs, 3);
        assert_eq!(config.max_retries, 5);
    }

    #[test]
    fn test_config_validation() {
        let config = Config::default();
        assert!(config.validate().is_ok());

        let invalid = Config {
            sync_interval_secs: 0,
            ..Default::default()
        };
        assert!(invalid.validate().is_err());
    }
}