use crate::{
models::error::WayclipError,
settings::{
api::ApiSettings, discovery::GameDiscovery, migration::SettingsMigrate,
notifications::NotificationSettings, output::OutputSettings, recording::RecordingSettings,
registry::SettingsRegistry, shortcut::ShortcutsSettings, tray::TraySettings,
},
};
use dirs::config_dir;
use semver::Version;
use serde::{Deserialize, Serialize, de::DeserializeOwned};
use std::{
fmt::Display,
fs::{read_to_string, write},
path::PathBuf,
};
const VERSION: &str = env!("CARGO_PKG_VERSION");
const DEFAULT_CONFIG_PATH: &str = "wayclip/config.json";
pub mod api;
pub mod discovery;
pub mod migration;
pub mod notifications;
pub mod output;
pub mod recording;
pub mod registry;
pub mod schema;
pub mod shortcut;
pub mod tray;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserSettings {
pub v: semver::Version,
pub api: ApiSettings,
pub recording: RecordingSettings,
pub output: OutputSettings,
pub shortcuts: ShortcutsSettings,
pub game_discovery: GameDiscovery,
pub notification: NotificationSettings,
pub tray: TraySettings,
}
impl Default for UserSettings {
fn default() -> Self {
Self {
v: Version::parse(VERSION).expect("Could not parse VERSION"),
api: ApiSettings::default(),
recording: RecordingSettings::default(),
output: OutputSettings::default(),
shortcuts: ShortcutsSettings::default(),
game_discovery: GameDiscovery::default(),
notification: NotificationSettings::default(),
tray: TraySettings::default(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Ver {
v: semver::Version,
}
impl UserSettings {
pub fn load() -> Result<Self, WayclipError> {
let file = Self::config_path()?;
if file.exists() {
let output = read_to_string(file)?;
let string = output.as_str();
let v = serde_json::from_str::<Ver>(string)
.map_err(|e| WayclipError::Config(format!("Config file corrupted: {e}").into()))?
.v;
let mut config: serde_json::Value = serde_json::from_str(&output)?;
SettingsMigrate::migrate(&mut config, v, Version::parse(VERSION)?)?;
let settings: UserSettings = serde_json::from_value(config)?;
Ok(settings)
} else {
let settings = Self::default();
Self::save_to_local_disk(&settings)?;
Ok(settings)
}
}
pub fn save_to_local_disk(&self) -> Result<(), WayclipError> {
let file = Self::config_path()?;
if let Some(parent) = file.parent() {
std::fs::create_dir_all(parent)?;
}
let pretty_json = serde_json::to_string_pretty(self)?;
write(file, pretty_json)?;
Ok(())
}
pub fn set_value(&mut self, key: &str, value: serde_json::Value) -> Result<(), WayclipError> {
let (_, def) = SettingsRegistry::find_by_key(key)
.ok_or_else(|| WayclipError::NotFound(format!("No such key: {key}").into()))?;
let parsed_value = SettingsRegistry::parse_raw_value(key, value)?;
let target_path = if def.location.is_empty() {
def.field_name.clone()
} else {
format!("{}.{}", def.location, def.field_name)
};
*self = SettingsRegistry::set_value(self, &target_path, parsed_value)?;
Ok(())
}
pub fn set_str(&mut self, key: &str, value: &str) -> Result<(), WayclipError> {
let (_, def) = SettingsRegistry::find_by_key(key)
.ok_or_else(|| WayclipError::NotFound(format!("No such key: {key}").into()))?;
let parsed_value = SettingsRegistry::parse_raw_str(key, value)?;
let target_path = if def.location.is_empty() {
def.field_name.clone()
} else {
format!("{}.{}", def.location, def.field_name)
};
*self = SettingsRegistry::set_value(self, &target_path, parsed_value)?;
Ok(())
}
pub fn get<R: DeserializeOwned + Send + 'static + Display>(
&self,
key: &str,
) -> Result<R, WayclipError> {
let (_, def) = SettingsRegistry::find_by_key(key)
.ok_or_else(|| WayclipError::NotFound(format!("No such key: {key}").into()))?;
let target_path = if def.location.is_empty() {
def.field_name.clone()
} else {
format!("{}.{}", def.location, def.field_name)
};
SettingsRegistry::get_value(self, &target_path)
}
pub fn config_path() -> Result<PathBuf, WayclipError> {
Ok(config_dir()
.expect("No config dir found..")
.join(DEFAULT_CONFIG_PATH))
}
}