1use crate::error::NetPulseError;
8use serde::{Deserialize, Serialize};
9use std::path::Path;
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct NetPulseConfig {
14 #[serde(default)]
16 pub targets: Vec<String>,
17
18 #[serde(default = "default_interval_ms")]
20 pub interval_ms: u64,
21
22 #[serde(default = "default_timeout_ms")]
24 pub timeout_ms: u64,
25
26 #[serde(default = "default_window_size")]
28 pub window_size: usize,
29
30 #[serde(default = "default_report_every")]
32 pub report_every: u64,
33
34 #[serde(default = "default_probe_type")]
36 pub probe_type: ProbeType,
37
38 #[serde(default = "default_tcp_port")]
40 pub tcp_port: u16,
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
45#[serde(rename_all = "lowercase")]
46pub enum ProbeType {
47 Icmp,
48 Tcp,
49 Udp,
50}
51
52impl std::fmt::Display for ProbeType {
53 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54 match self {
55 ProbeType::Icmp => write!(f, "icmp"),
56 ProbeType::Tcp => write!(f, "tcp"),
57 ProbeType::Udp => write!(f, "udp"),
58 }
59 }
60}
61
62impl std::str::FromStr for ProbeType {
63 type Err = NetPulseError;
64
65 fn from_str(s: &str) -> Result<Self, Self::Err> {
66 match s.to_lowercase().as_str() {
67 "icmp" => Ok(ProbeType::Icmp),
68 "tcp" => Ok(ProbeType::Tcp),
69 "udp" => Ok(ProbeType::Udp),
70 _ => Err(NetPulseError::ConfigError(format!(
71 "unknown probe type '{}', must be 'icmp', 'tcp', or 'udp'",
72 s
73 ))),
74 }
75 }
76}
77
78fn default_interval_ms() -> u64 {
79 1000
80}
81fn default_timeout_ms() -> u64 {
82 3000
83}
84fn default_window_size() -> usize {
85 300
86} fn default_report_every() -> u64 {
88 10
89} fn default_probe_type() -> ProbeType {
91 ProbeType::Icmp
92}
93fn default_tcp_port() -> u16 {
94 80
95}
96
97impl Default for NetPulseConfig {
98 fn default() -> Self {
99 Self {
100 targets: vec![],
101 interval_ms: default_interval_ms(),
102 timeout_ms: default_timeout_ms(),
103 window_size: default_window_size(),
104 report_every: default_report_every(),
105 probe_type: default_probe_type(),
106 tcp_port: default_tcp_port(),
107 }
108 }
109}
110
111impl NetPulseConfig {
112 pub fn from_file(path: &Path) -> Result<Self, NetPulseError> {
114 if !path.exists() {
115 return Ok(Self::default());
116 }
117
118 let content = std::fs::read_to_string(path)
119 .map_err(|e| NetPulseError::ConfigError(format!("cannot read config file: {}", e)))?;
120
121 toml::from_str(&content)
122 .map_err(|e| NetPulseError::ConfigError(format!("invalid TOML: {}", e)))
123 }
124}