use ratatui::style::Color;
use serde::{Deserialize, Serialize};
use serde_inline_default::serde_inline_default;
const COLORS_OPTIONS: [Color; 7] =
[Color::Green, Color::Yellow, Color::Red, Color::Blue, Color::Magenta, Color::Cyan, Color::Reset];
pub(crate) const TUI_MIN_MS: u32 = 250;
pub(crate) const TUI_MAX_MS: u32 = 10_000;
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub enum ViewType {
Sparkline,
Gauge,
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Copy)]
pub enum RatioMode {
Scaled,
Active,
}
impl RatioMode {
pub fn label(self) -> &'static str {
match self {
Self::Scaled => "scaled",
Self::Active => "active",
}
}
}
#[serde_inline_default]
#[derive(Debug, Serialize, Deserialize)]
pub struct Config {
#[serde_inline_default(ViewType::Sparkline)]
pub view_type: ViewType,
#[serde_inline_default(COLORS_OPTIONS[0])]
pub color: Color,
#[serde_inline_default(1000)]
pub interval: u32,
#[serde_inline_default(false)]
pub per_core_view: bool,
#[serde_inline_default(RatioMode::Scaled)]
pub ratio_mode: RatioMode,
}
impl Default for Config {
fn default() -> Self {
serde_json::from_str("{}").unwrap()
}
}
impl Config {
fn normalize(mut self) -> Self {
self.interval = self.interval.clamp(TUI_MIN_MS, TUI_MAX_MS);
self
}
fn get_config_path() -> Option<String> {
let home = match std::env::var("HOME") {
Ok(home) => home,
Err(_) => return None,
};
let filepath = format!("{}/.config/macmon.json", home);
let _ = std::fs::create_dir_all(std::path::Path::new(&filepath).parent().unwrap());
Some(filepath)
}
pub fn load() -> Self {
if let Some(path) = Self::get_config_path() {
let file = match std::fs::File::open(path) {
Ok(file) => file,
Err(_) => return Self::default().normalize(),
};
let reader = std::io::BufReader::new(file);
let cfg: Self = serde_json::from_reader(reader).unwrap_or_default();
return cfg.normalize();
}
Self::default().normalize()
}
pub fn save(&self) {
if let Some(path) = Self::get_config_path() {
let file = match std::fs::File::create(path) {
Ok(file) => file,
Err(_) => return,
};
let writer = std::io::BufWriter::new(file);
let _ = serde_json::to_writer_pretty(writer, self);
}
}
pub fn next_color(&mut self) {
self.color = match COLORS_OPTIONS.iter().position(|&c| c == self.color) {
Some(idx) => COLORS_OPTIONS[(idx + 1) % COLORS_OPTIONS.len()],
None => COLORS_OPTIONS[0],
};
self.save();
}
pub fn next_view_type(&mut self) {
self.view_type = match self.view_type {
ViewType::Sparkline => ViewType::Gauge,
ViewType::Gauge => ViewType::Sparkline,
};
self.save();
}
pub fn dec_interval(&mut self) {
let step = 250;
self.interval = (self.interval.saturating_sub(step).div_ceil(step) * step).max(TUI_MIN_MS);
self.save();
}
pub fn inc_interval(&mut self) {
let step = 250;
self.interval = (self.interval.saturating_add(step) / step * step).min(TUI_MAX_MS);
self.save();
}
pub fn toggle_per_core_view(&mut self) {
self.per_core_view = !self.per_core_view;
self.save();
}
pub fn toggle_ratio_mode(&mut self) {
self.ratio_mode = match self.ratio_mode {
RatioMode::Scaled => RatioMode::Active,
RatioMode::Active => RatioMode::Scaled,
};
self.save();
}
}