use serde::{Deserialize, Serialize};
use std::{
fs,
net::{Ipv4Addr, SocketAddr},
path::PathBuf,
};
use tracing::warn;
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[allow(clippy::upper_case_acronyms)]
pub enum ShutdownSignal {
SIGUSR1,
SIGUSR2,
SIGTERM,
}
impl ShutdownSignal {
#[cfg(any(target_os = "linux", target_os = "macos"))]
pub fn to_signal(self) -> core::ffi::c_int {
match self {
Self::SIGUSR1 => signal_hook::consts::SIGUSR1,
Self::SIGUSR2 => signal_hook::consts::SIGUSR2,
Self::SIGTERM => signal_hook::consts::SIGTERM,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(default)]
pub struct Settings {
pub update_shutdown_grace_period_secs: u32,
pub update_shutdown_message: String,
pub web_address: SocketAddr,
pub web_chat_secret: Option<String>,
pub ui_api_secret: Option<String>,
pub shutdown_signals: Vec<ShutdownSignal>,
}
impl Default for Settings {
fn default() -> Self {
Self {
update_shutdown_grace_period_secs: 120,
update_shutdown_message: "The server is restarting for an update".to_owned(),
web_address: SocketAddr::from((Ipv4Addr::LOCALHOST, 14005)),
web_chat_secret: None,
ui_api_secret: None,
shutdown_signals: if cfg!(any(target_os = "linux", target_os = "macos")) {
vec![ShutdownSignal::SIGUSR1]
} else {
Vec::new()
},
}
}
}
impl Settings {
pub fn load() -> Self {
let path = Self::get_settings_path();
if let Ok(file) = fs::File::open(&path) {
match ron::de::from_reader(file) {
Ok(s) => return s,
Err(e) => {
warn!(?e, "Failed to parse setting file! Fallback to default.");
let mut new_path = path.to_owned();
new_path.pop();
new_path.push("settings.invalid.ron");
if let Err(e) = fs::rename(&path, &new_path) {
warn!(?e, ?path, ?new_path, "Failed to rename settings file.");
}
},
}
}
let default_settings = Self::default();
default_settings.save_to_file_warn();
default_settings
}
fn save_to_file_warn(&self) {
if let Err(e) = self.save_to_file() {
warn!(?e, "Failed to save settings");
}
}
fn save_to_file(&self) -> std::io::Result<()> {
let path = Self::get_settings_path();
if let Some(dir) = path.parent() {
fs::create_dir_all(dir)?;
}
let ron = ron::ser::to_string_pretty(self, ron::ser::PrettyConfig::default()).unwrap();
fs::write(path, ron.as_bytes())
}
pub fn get_settings_path() -> PathBuf {
let mut path = data_dir();
path.push("settings.ron");
path
}
}
pub fn data_dir() -> PathBuf {
let mut path = common_base::userdata_dir_workspace!();
path.push("server-cli");
path
}