use super::{Colors, Font, KeybindsRaw, Panel};
use crate::error::Result;
use directories::BaseDirs;
use ron::{
de::from_reader,
extensions::Extensions,
ser::{to_string_pretty, to_writer_pretty, PrettyConfig},
};
use serde::{Deserialize, Serialize};
use std::{fs::OpenOptions, io::Write, path::PathBuf};
#[derive(Serialize, Deserialize, Debug)]
#[serde(default)]
pub struct Config {
def_file: Option<PathBuf>,
win_size: (u32, u32),
#[cfg(feature = "bg")]
img_file: Option<PathBuf>,
#[cfg(feature = "bg")]
img_scaled: bool,
inline_splits: bool,
colors: Colors,
frame_rounding: Option<u128>,
panels: Vec<Panel>,
#[serde(default = "Font::timer_default")]
t_font: Font,
#[serde(default = "Font::splits_default")]
s_font: Font,
ms_ratio: f32,
confirm_exit: bool,
plugins: bool,
binds: KeybindsRaw,
}
impl Config {
pub fn open() -> Result<Self> {
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(config_path()?)?;
Ok(from_reader(&file)?)
}
pub fn file(&self) -> Option<&PathBuf> {
self.def_file.as_ref()
}
#[cfg(feature = "bg")]
pub fn img(&self) -> Option<&PathBuf> {
self.img_file.as_ref()
}
#[cfg(feature = "bg")]
pub fn img_scaled(&self) -> bool {
self.img_scaled
}
pub fn set_file(&mut self, file: impl Into<PathBuf>) {
self.def_file = Some(file.into());
}
pub fn tfont(&self) -> &Font {
&self.t_font
}
pub fn sfont(&self) -> &Font {
&self.s_font
}
pub fn colors(&self) -> Colors {
self.colors
}
pub fn save(&self) -> Result<()> {
let mut file = OpenOptions::new()
.write(true)
.truncate(true)
.open(config_path()?)?;
let string = to_string_pretty(
self,
PrettyConfig::new().extensions(Extensions::IMPLICIT_SOME),
)?;
file.write_all(string.as_bytes())?;
Ok(())
}
pub fn binds(&self) -> &KeybindsRaw {
&self.binds
}
pub fn inline_splits(&self) -> bool {
self.inline_splits
}
pub fn panels(&self) -> &Vec<Panel> {
&self.panels
}
pub fn rounding(&self) -> Option<u128> {
self.frame_rounding
}
pub fn ms_ratio(&self) -> f32 {
self.ms_ratio
}
pub fn win_size(&self) -> (u32, u32) {
self.win_size
}
pub fn set_win_size(&mut self, new: (u32, u32)) {
self.win_size = new;
}
pub fn confirm(&self) -> bool {
self.confirm_exit
}
pub fn plugins(&self) -> bool {
self.plugins
}
}
impl Default for Config {
fn default() -> Config {
Config {
def_file: None,
win_size: (300, 500),
#[cfg(feature = "bg")]
img_file: None,
#[cfg(feature = "bg")]
img_scaled: false,
frame_rounding: None,
colors: Colors::default(),
inline_splits: false,
panels: vec![],
t_font: Font::timer_default(),
s_font: Font::splits_default(),
ms_ratio: 1.0,
confirm_exit: true,
plugins: true,
binds: KeybindsRaw::default(),
}
}
}
fn config_path() -> Result<PathBuf> {
let cfg_path = get_config_path()?;
if !cfg_path.exists() {
let f = OpenOptions::new()
.create(true)
.write(true)
.open(&cfg_path)?;
to_writer_pretty(
f,
&Config::default(),
PrettyConfig::new().extensions(Extensions::IMPLICIT_SOME),
)?;
}
Ok(cfg_path)
}
fn get_config_path() -> Result<PathBuf> {
#[cfg(feature = "portable")]
if let Ok(mut p) = std::env::current_exe() {
p.pop();
p.push("mist.cfg");
return Ok(p);
}
let dirs = BaseDirs::new().ok_or("Could not find your config directory!")?;
let mut cfg_path = dirs.config_dir().to_path_buf();
cfg_path.push("mist");
if !cfg_path.exists() {
std::fs::create_dir_all(&cfg_path)?;
}
cfg_path.push("mist.cfg");
Ok(cfg_path)
}
pub fn get_plugin_dir() -> Result<PathBuf> {
let mut cfg_path = get_config_path()?;
cfg_path.pop();
cfg_path.push("plugins");
Ok(cfg_path)
}