#[allow(dead_code)] pub struct AppConfig;
impl AppConfig {
pub const MIN_RENDER_INTERVAL_MS: u64 = 100; #[allow(dead_code)] pub const EVENT_POLL_TIMEOUT_MS: u64 = 100; pub const SCROLL_UPDATE_FREQUENCY: u64 = 2;
pub const ANIMATION_TICK_MS: u64 = 200;
pub const REFRESH_TICK_MS: u64 = 1000;
pub const TERMINAL_READER_POLL_MS: u64 = 50;
pub const BACKEND_AI_DEFAULT_PORT: u16 = 9090;
pub const MAX_CONCURRENT_CONNECTIONS: usize = 128;
pub const CONNECTION_TIMEOUT_SECS: u64 = 5;
pub const POOL_IDLE_TIMEOUT_SECS: u64 = 60;
pub const POOL_MAX_IDLE_PER_HOST: usize = 200;
pub const TCP_KEEPALIVE_SECS: u64 = 30;
pub const HTTP2_KEEPALIVE_SECS: u64 = 30;
pub const RETRY_ATTEMPTS: u32 = 3;
pub const RETRY_BASE_DELAY_MS: u64 = 50;
#[allow(dead_code)] pub const DEFAULT_UPDATE_INTERVAL_SECS: u64 = 2;
pub const HISTORY_MAX_ENTRIES: usize = 1024;
pub const CONNECTION_STAGGER_BASE_MS: u64 = 500;
pub const PROGRESS_BAR_LABEL_WIDTH: usize = 5;
pub const PROGRESS_BAR_BRACKET_WIDTH: usize = 4; pub const PROGRESS_BAR_TEXT_WIDTH: usize = 8;
#[allow(dead_code)] pub const DASHBOARD_ITEM_WIDTH: usize = 15;
#[allow(dead_code)] pub const DEFAULT_TERMINAL_WIDTH: u16 = 80;
#[allow(dead_code)] pub const DEFAULT_TERMINAL_HEIGHT: u16 = 24;
#[allow(dead_code)] pub const LINUX_PAGE_SIZE_BYTES: u64 = 4096;
#[allow(dead_code)] pub const LINUX_JIFFIES_PER_SECOND: u64 = 100;
#[allow(dead_code)] pub const NOTIFICATION_DURATION_SECS: u64 = 5;
pub const CRITICAL_THRESHOLD: f64 = 0.8;
pub const WARNING_THRESHOLD: f64 = 0.7;
pub const NORMAL_THRESHOLD: f64 = 0.25;
pub const LOW_THRESHOLD: f64 = 0.05;
}
#[derive(Clone, Debug)]
pub struct AlertConfig {
pub temp_warn_c: u32,
pub temp_crit_c: u32,
pub util_idle_pct: u32,
pub util_idle_warn_mins: u32,
pub power_crit_w: u32,
pub bell_on_critical: bool,
pub webhook_url: String,
pub hysteresis_c: u32,
pub flash_duration_secs: u64,
}
impl Default for AlertConfig {
fn default() -> Self {
Self {
temp_warn_c: 80,
temp_crit_c: 90,
util_idle_pct: 5,
util_idle_warn_mins: 15,
power_crit_w: 0,
bell_on_critical: false,
webhook_url: String::new(),
hysteresis_c: 2,
flash_duration_secs: 2,
}
}
}
impl AlertConfig {
pub fn with_cli_overrides(
mut self,
alert_temp: Option<u32>,
alert_util_low_mins: Option<u32>,
) -> Self {
if let Some(t) = alert_temp {
self.temp_warn_c = t;
if self.temp_crit_c < t + 5 {
self.temp_crit_c = t + 10;
}
}
if let Some(m) = alert_util_low_mins {
self.util_idle_warn_mins = m;
}
self
}
}
#[derive(Clone, Debug)]
pub struct EnergyConfig {
pub price_per_kwh: f64,
pub currency: String,
pub show_cost: bool,
pub wal_path: Option<String>,
pub gap_interpolate_seconds: u64,
pub wal_enabled: bool,
}
impl Default for EnergyConfig {
fn default() -> Self {
Self {
price_per_kwh: 0.12,
currency: "USD".to_string(),
show_cost: true,
wal_path: None,
gap_interpolate_seconds: 10,
wal_enabled: true,
}
}
}
impl EnergyConfig {
pub fn with_env_overrides(mut self) -> Self {
if let Ok(v) = std::env::var("ALL_SMI_ENERGY_PRICE")
&& let Ok(price) = v.parse::<f64>()
&& price.is_finite()
&& price >= 0.0
{
self.price_per_kwh = price;
}
if let Ok(v) = std::env::var("ALL_SMI_ENERGY_CURRENCY")
&& !v.trim().is_empty()
{
self.currency = v.trim().to_string();
}
if std::env::var("ALL_SMI_ENERGY_NO_COST").is_ok() {
self.show_cost = false;
}
if let Ok(v) = std::env::var("ALL_SMI_ENERGY_WAL_PATH")
&& !v.trim().is_empty()
{
self.wal_path = Some(v.trim().to_string());
}
if std::env::var("ALL_SMI_ENERGY_NO_WAL").is_ok() {
self.wal_enabled = false;
}
if let Ok(v) = std::env::var("ALL_SMI_ENERGY_GAP_SECONDS")
&& let Ok(secs) = v.parse::<u64>()
&& secs > 0
&& secs <= 3600
{
self.gap_interpolate_seconds = secs;
}
self
}
pub fn cost_visible(&self) -> bool {
self.show_cost && self.price_per_kwh.is_finite() && self.price_per_kwh > 0.0
}
}
#[allow(dead_code)] pub struct EnvConfig;
impl EnvConfig {
pub fn adaptive_interval(node_count: usize) -> u64 {
match node_count {
0 => {
if cfg!(target_os = "macos") && crate::device::is_apple_silicon() {
1
} else {
2
}
}
1..=10 => 3, 11..=50 => 4,
51..=100 => 5,
_ => 6,
}
}
pub fn local_interval() -> u64 {
Self::adaptive_interval(0)
}
#[allow(dead_code)] pub fn max_concurrent_connections(total_hosts: usize) -> usize {
std::cmp::min(total_hosts, AppConfig::MAX_CONCURRENT_CONNECTIONS)
}
pub fn connection_stagger_delay(host_index: usize, total_hosts: usize) -> u64 {
(host_index as u64 * AppConfig::CONNECTION_STAGGER_BASE_MS) / total_hosts as u64
}
pub fn retry_delay(attempt: u32) -> u64 {
AppConfig::RETRY_BASE_DELAY_MS * attempt as u64
}
}
#[cfg(feature = "cli")]
pub struct ThemeConfig;
#[cfg(feature = "cli")]
impl ThemeConfig {
pub fn accent_color() -> crossterm::style::Color {
crossterm::style::Color::Cyan
}
pub fn cpu_color() -> crossterm::style::Color {
crossterm::style::Color::Cyan
}
pub fn gpu_color() -> crossterm::style::Color {
crossterm::style::Color::Blue
}
pub fn memory_color() -> crossterm::style::Color {
crossterm::style::Color::Green
}
pub fn power_color() -> crossterm::style::Color {
crossterm::style::Color::Red
}
pub fn thermal_color() -> crossterm::style::Color {
crossterm::style::Color::Magenta
}
pub fn accelerator_color() -> crossterm::style::Color {
crossterm::style::Color::Yellow
}
pub fn progress_bar_color(fill_ratio: f64) -> crossterm::style::Color {
use crossterm::style::Color;
if fill_ratio > AppConfig::CRITICAL_THRESHOLD {
Color::Red
} else if fill_ratio > AppConfig::WARNING_THRESHOLD {
Color::Yellow
} else if fill_ratio > AppConfig::NORMAL_THRESHOLD {
Color::Green
} else if fill_ratio > AppConfig::LOW_THRESHOLD {
Color::DarkGreen
} else {
Color::DarkGrey
}
}
pub fn utilization_color(utilization: f64) -> crossterm::style::Color {
use crossterm::style::Color;
if utilization > 80.0 {
Color::Red
} else if utilization > 50.0 {
Color::Yellow
} else if utilization > 20.0 {
Color::Green
} else {
Color::DarkGrey
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_adaptive_interval() {
let expected_local = if cfg!(target_os = "macos") && crate::device::is_apple_silicon() {
1
} else {
2
};
assert_eq!(EnvConfig::adaptive_interval(0), expected_local);
assert_eq!(EnvConfig::adaptive_interval(1), 3); assert_eq!(EnvConfig::adaptive_interval(2), 3);
assert_eq!(EnvConfig::adaptive_interval(5), 3);
assert_eq!(EnvConfig::adaptive_interval(10), 3);
assert_eq!(EnvConfig::adaptive_interval(11), 4);
assert_eq!(EnvConfig::adaptive_interval(25), 4);
assert_eq!(EnvConfig::adaptive_interval(50), 4);
assert_eq!(EnvConfig::adaptive_interval(51), 5);
assert_eq!(EnvConfig::adaptive_interval(75), 5);
assert_eq!(EnvConfig::adaptive_interval(100), 5);
assert_eq!(EnvConfig::adaptive_interval(101), 6);
assert_eq!(EnvConfig::adaptive_interval(200), 6);
assert_eq!(EnvConfig::adaptive_interval(500), 6);
assert_eq!(EnvConfig::adaptive_interval(1000), 6);
}
#[test]
fn test_local_interval_is_the_no_remote_nodes_arm() {
assert_eq!(EnvConfig::local_interval(), EnvConfig::adaptive_interval(0));
assert_ne!(
EnvConfig::local_interval(),
EnvConfig::adaptive_interval(1),
"local cadence must differ from the 1-to-10-remote-nodes cadence"
);
assert!(
EnvConfig::local_interval() <= 2,
"local monitoring polls at 1s (Apple Silicon) or 2s, got {}",
EnvConfig::local_interval()
);
}
#[test]
fn test_max_concurrent_connections() {
assert_eq!(EnvConfig::max_concurrent_connections(10), 10);
assert_eq!(EnvConfig::max_concurrent_connections(50), 50);
assert_eq!(EnvConfig::max_concurrent_connections(64), 64);
assert_eq!(EnvConfig::max_concurrent_connections(100), 100);
assert_eq!(EnvConfig::max_concurrent_connections(128), 128);
assert_eq!(EnvConfig::max_concurrent_connections(200), 128);
}
#[test]
fn test_connection_stagger_delay() {
assert_eq!(EnvConfig::connection_stagger_delay(0, 10), 0);
assert_eq!(EnvConfig::connection_stagger_delay(1, 10), 50);
assert_eq!(EnvConfig::connection_stagger_delay(5, 10), 250);
assert_eq!(EnvConfig::connection_stagger_delay(9, 10), 450);
assert_eq!(EnvConfig::connection_stagger_delay(0, 1), 0);
assert_eq!(EnvConfig::connection_stagger_delay(10, 20), 250);
}
#[test]
fn test_retry_delay() {
assert_eq!(EnvConfig::retry_delay(1), 50);
assert_eq!(EnvConfig::retry_delay(2), 100);
assert_eq!(EnvConfig::retry_delay(3), 150);
assert_eq!(EnvConfig::retry_delay(5), 250);
assert_eq!(EnvConfig::retry_delay(0), 0);
}
#[test]
#[cfg(feature = "cli")]
fn test_progress_bar_color_thresholds() {
use crossterm::style::Color;
assert_eq!(ThemeConfig::progress_bar_color(0.0), Color::DarkGrey);
assert_eq!(ThemeConfig::progress_bar_color(0.03), Color::DarkGrey);
assert_eq!(ThemeConfig::progress_bar_color(0.05), Color::DarkGrey);
assert_eq!(ThemeConfig::progress_bar_color(0.06), Color::DarkGreen);
assert_eq!(ThemeConfig::progress_bar_color(0.1), Color::DarkGreen);
assert_eq!(ThemeConfig::progress_bar_color(0.25), Color::DarkGreen);
assert_eq!(ThemeConfig::progress_bar_color(0.26), Color::Green);
assert_eq!(ThemeConfig::progress_bar_color(0.5), Color::Green);
assert_eq!(ThemeConfig::progress_bar_color(0.7), Color::Green);
assert_eq!(ThemeConfig::progress_bar_color(0.71), Color::Yellow);
assert_eq!(ThemeConfig::progress_bar_color(0.75), Color::Yellow);
assert_eq!(ThemeConfig::progress_bar_color(0.8), Color::Yellow);
assert_eq!(ThemeConfig::progress_bar_color(0.81), Color::Red);
assert_eq!(ThemeConfig::progress_bar_color(0.9), Color::Red);
assert_eq!(ThemeConfig::progress_bar_color(1.0), Color::Red);
}
#[test]
#[cfg(feature = "cli")]
fn test_utilization_color_thresholds() {
use crossterm::style::Color;
assert_eq!(ThemeConfig::utilization_color(0.0), Color::DarkGrey);
assert_eq!(ThemeConfig::utilization_color(10.0), Color::DarkGrey);
assert_eq!(ThemeConfig::utilization_color(20.0), Color::DarkGrey);
assert_eq!(ThemeConfig::utilization_color(20.1), Color::Green);
assert_eq!(ThemeConfig::utilization_color(30.0), Color::Green);
assert_eq!(ThemeConfig::utilization_color(50.0), Color::Green);
assert_eq!(ThemeConfig::utilization_color(50.1), Color::Yellow);
assert_eq!(ThemeConfig::utilization_color(70.0), Color::Yellow);
assert_eq!(ThemeConfig::utilization_color(80.0), Color::Yellow);
assert_eq!(ThemeConfig::utilization_color(80.1), Color::Red);
assert_eq!(ThemeConfig::utilization_color(90.0), Color::Red);
assert_eq!(ThemeConfig::utilization_color(100.0), Color::Red);
}
#[test]
fn test_alert_config_defaults() {
let cfg = AlertConfig::default();
assert_eq!(cfg.temp_warn_c, 80);
assert_eq!(cfg.temp_crit_c, 90);
assert_eq!(cfg.util_idle_pct, 5);
assert_eq!(cfg.util_idle_warn_mins, 15);
assert_eq!(cfg.power_crit_w, 0);
assert!(!cfg.bell_on_critical);
assert!(cfg.webhook_url.is_empty());
assert_eq!(cfg.hysteresis_c, 2);
assert_eq!(cfg.flash_duration_secs, 2);
}
#[test]
fn test_alert_config_cli_overrides_apply() {
let cfg = AlertConfig::default().with_cli_overrides(Some(70), Some(30));
assert_eq!(cfg.temp_warn_c, 70);
assert!(cfg.temp_crit_c > cfg.temp_warn_c);
assert_eq!(cfg.util_idle_warn_mins, 30);
}
#[test]
fn test_alert_config_cli_overrides_no_change_when_none() {
let original = AlertConfig::default();
let cfg = original.clone().with_cli_overrides(None, None);
assert_eq!(cfg.temp_warn_c, original.temp_warn_c);
assert_eq!(cfg.temp_crit_c, original.temp_crit_c);
assert_eq!(cfg.util_idle_warn_mins, original.util_idle_warn_mins);
}
#[test]
fn test_app_config_constants() {
assert_eq!(AppConfig::MIN_RENDER_INTERVAL_MS, 100);
assert_eq!(AppConfig::EVENT_POLL_TIMEOUT_MS, 100);
assert_eq!(AppConfig::MAX_CONCURRENT_CONNECTIONS, 128);
assert_eq!(AppConfig::CONNECTION_TIMEOUT_SECS, 5);
assert_eq!(AppConfig::RETRY_ATTEMPTS, 3);
assert_eq!(AppConfig::RETRY_BASE_DELAY_MS, 50);
assert_eq!(AppConfig::DEFAULT_UPDATE_INTERVAL_SECS, 2);
assert_eq!(AppConfig::CONNECTION_STAGGER_BASE_MS, 500);
assert_eq!(AppConfig::CRITICAL_THRESHOLD, 0.8);
assert_eq!(AppConfig::WARNING_THRESHOLD, 0.7);
assert_eq!(AppConfig::NORMAL_THRESHOLD, 0.25);
assert_eq!(AppConfig::LOW_THRESHOLD, 0.05);
}
#[test]
fn test_boundary_conditions() {
let expected_local = if cfg!(target_os = "macos") && crate::device::is_apple_silicon() {
1
} else {
2
};
assert_eq!(EnvConfig::adaptive_interval(0), expected_local);
assert_eq!(EnvConfig::adaptive_interval(usize::MAX), 6);
}
#[test]
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
fn test_apple_silicon_adaptive_interval() {
assert_eq!(EnvConfig::adaptive_interval(0), 1);
assert_eq!(EnvConfig::adaptive_interval(1), 3);
assert_eq!(EnvConfig::adaptive_interval(2), 3);
assert_eq!(EnvConfig::adaptive_interval(10), 3);
}
#[test]
#[cfg(not(all(target_os = "macos", target_arch = "aarch64")))]
fn test_non_apple_silicon_adaptive_interval() {
assert_eq!(EnvConfig::adaptive_interval(0), 2);
assert_eq!(EnvConfig::adaptive_interval(1), 3);
assert_eq!(EnvConfig::adaptive_interval(2), 3);
assert_eq!(EnvConfig::adaptive_interval(10), 3);
assert_eq!(EnvConfig::connection_stagger_delay(0, 1), 0);
assert_eq!(EnvConfig::connection_stagger_delay(1000, 1000), 500);
assert_eq!(EnvConfig::retry_delay(0), 0);
assert_eq!(EnvConfig::retry_delay(1000), 50000);
}
#[test]
fn energy_config_defaults_match_issue_spec() {
let cfg = EnergyConfig::default();
assert!((cfg.price_per_kwh - 0.12).abs() < 1e-9);
assert_eq!(cfg.currency, "USD");
assert!(cfg.show_cost);
assert_eq!(cfg.wal_path, None);
assert_eq!(cfg.gap_interpolate_seconds, 10);
assert!(cfg.wal_enabled);
assert!(cfg.cost_visible());
}
#[test]
fn energy_config_cost_hidden_when_price_zero_or_invalid() {
let cfg = EnergyConfig {
price_per_kwh: 0.0,
..EnergyConfig::default()
};
assert!(!cfg.cost_visible(), "zero price must hide cost");
let cfg = EnergyConfig {
price_per_kwh: -0.5,
..EnergyConfig::default()
};
assert!(!cfg.cost_visible(), "negative price must hide cost");
let cfg = EnergyConfig {
price_per_kwh: f64::NAN,
..EnergyConfig::default()
};
assert!(!cfg.cost_visible(), "NaN price must hide cost");
let cfg = EnergyConfig {
show_cost: false,
..EnergyConfig::default()
};
assert!(!cfg.cost_visible(), "show_cost=false must hide cost");
}
#[test]
fn energy_config_env_overrides_apply() {
let _guard = crate::common::test_env::lock_env();
let keys = [
"ALL_SMI_ENERGY_PRICE",
"ALL_SMI_ENERGY_CURRENCY",
"ALL_SMI_ENERGY_NO_COST",
"ALL_SMI_ENERGY_WAL_PATH",
"ALL_SMI_ENERGY_NO_WAL",
"ALL_SMI_ENERGY_GAP_SECONDS",
];
unsafe {
for k in keys {
std::env::remove_var(k);
}
std::env::set_var("ALL_SMI_ENERGY_PRICE", "0.30");
std::env::set_var("ALL_SMI_ENERGY_CURRENCY", "KRW");
std::env::set_var("ALL_SMI_ENERGY_NO_COST", "1");
std::env::set_var("ALL_SMI_ENERGY_WAL_PATH", "/tmp/unit-wal.bin");
std::env::set_var("ALL_SMI_ENERGY_NO_WAL", "1");
std::env::set_var("ALL_SMI_ENERGY_GAP_SECONDS", "15");
}
let cfg = EnergyConfig::default().with_env_overrides();
assert!((cfg.price_per_kwh - 0.30).abs() < 1e-9);
assert_eq!(cfg.currency, "KRW");
assert!(!cfg.show_cost, "ALL_SMI_ENERGY_NO_COST must disable cost");
assert_eq!(cfg.wal_path.as_deref(), Some("/tmp/unit-wal.bin"));
assert!(!cfg.wal_enabled, "ALL_SMI_ENERGY_NO_WAL must disable WAL");
assert_eq!(cfg.gap_interpolate_seconds, 15);
unsafe {
for k in keys {
std::env::remove_var(k);
}
}
}
#[test]
fn energy_config_gap_seconds_env_clamped_to_range() {
let _guard = crate::common::test_env::lock_env();
unsafe {
std::env::remove_var("ALL_SMI_ENERGY_GAP_SECONDS");
std::env::set_var("ALL_SMI_ENERGY_GAP_SECONDS", "7200");
}
let cfg = EnergyConfig::default().with_env_overrides();
assert_eq!(cfg.gap_interpolate_seconds, 10);
unsafe {
std::env::set_var("ALL_SMI_ENERGY_GAP_SECONDS", "0");
}
let cfg = EnergyConfig::default().with_env_overrides();
assert_eq!(cfg.gap_interpolate_seconds, 10);
unsafe {
std::env::set_var("ALL_SMI_ENERGY_GAP_SECONDS", "3600");
}
let cfg = EnergyConfig::default().with_env_overrides();
assert_eq!(cfg.gap_interpolate_seconds, 3600);
unsafe {
std::env::remove_var("ALL_SMI_ENERGY_GAP_SECONDS");
}
}
#[test]
fn energy_config_invalid_price_env_ignored() {
let _guard = crate::common::test_env::lock_env();
unsafe {
std::env::remove_var("ALL_SMI_ENERGY_PRICE");
std::env::set_var("ALL_SMI_ENERGY_PRICE", "not-a-number");
}
let cfg = EnergyConfig::default().with_env_overrides();
assert!((cfg.price_per_kwh - 0.12).abs() < 1e-9);
unsafe {
std::env::remove_var("ALL_SMI_ENERGY_PRICE");
}
}
#[test]
#[cfg(all(
feature = "cli",
not(all(target_os = "macos", target_arch = "aarch64"))
))]
fn test_non_apple_silicon_theme_boundary_values() {
use crossterm::style::Color;
assert_eq!(
ThemeConfig::progress_bar_color(f64::NEG_INFINITY),
Color::DarkGrey
);
assert_eq!(ThemeConfig::progress_bar_color(f64::INFINITY), Color::Red);
assert_eq!(
ThemeConfig::utilization_color(f64::NEG_INFINITY),
Color::DarkGrey
);
assert_eq!(ThemeConfig::utilization_color(f64::INFINITY), Color::Red);
assert_eq!(ThemeConfig::progress_bar_color(-1.0), Color::DarkGrey);
assert_eq!(ThemeConfig::progress_bar_color(2.0), Color::Red);
assert_eq!(ThemeConfig::utilization_color(-10.0), Color::DarkGrey);
assert_eq!(ThemeConfig::utilization_color(200.0), Color::Red);
}
}