118 lines
3.9 KiB
Rust
118 lines
3.9 KiB
Rust
use color_eyre::eyre::Error;
|
|
use std::collections::HashMap;
|
|
use tracing::info;
|
|
|
|
#[derive(Clone, PartialEq)]
|
|
pub struct NativeConfigSystem {
|
|
config_path: std::path::PathBuf,
|
|
}
|
|
|
|
impl super::ConfigSystemInterface for NativeConfigSystem {
|
|
fn new() -> color_eyre::Result<Self, Error> {
|
|
return Ok(NativeConfigSystem {
|
|
config_path: get_config_path()?,
|
|
});
|
|
}
|
|
|
|
fn config_get<T>(&self, key: &str) -> Option<T>
|
|
where
|
|
T: serde::de::DeserializeOwned,
|
|
{
|
|
let config = load_config_map(&self.config_path);
|
|
|
|
let Some(value_untyped) = config.get(key).cloned().or_else(|| config_get_default(key))
|
|
else {
|
|
return None;
|
|
};
|
|
|
|
match serde_json::from_value::<T>(value_untyped) {
|
|
Ok(v) => Some(v),
|
|
Err(_) => {
|
|
let default_value = config_get_default(key)
|
|
.expect("Default value required after config parse failure");
|
|
Some(
|
|
serde_json::from_value::<T>(default_value)
|
|
.expect("Default value could not be parsed"),
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
fn config_set<T>(&self, key: &str, value: &T)
|
|
where
|
|
T: serde::Serialize,
|
|
{
|
|
let mut config = load_config_map(&self.config_path);
|
|
let json_value = serde_json::to_value(value).expect("failed to serialize config value");
|
|
config.insert(key.to_string(), json_value);
|
|
save_config_map(&config).expect("failed to set config")
|
|
}
|
|
}
|
|
|
|
#[cfg(any(feature = "desktop"))]
|
|
fn get_config_path() -> color_eyre::Result<std::path::PathBuf> {
|
|
use etcetera::{choose_app_strategy, AppStrategy, AppStrategyArgs};
|
|
|
|
let strategy = choose_app_strategy(AppStrategyArgs {
|
|
top_level_domain: "xyz".to_string(),
|
|
author: "ohea".to_string(),
|
|
app_name: "Mumble Web2".to_string(),
|
|
})
|
|
.expect("failed to choose app strategy");
|
|
Ok(strategy.config_dir().join("config.json"))
|
|
}
|
|
|
|
#[cfg(target_os = "android")]
|
|
fn get_config_path() -> color_eyre::Result<std::path::PathBuf> {
|
|
let ctx = ndk_context::android_context();
|
|
let vm = unsafe { jni::JavaVM::from_raw(ctx.vm().cast()) }?;
|
|
let mut env = vm.attach_current_thread()?;
|
|
let ctx = unsafe { jni::objects::JObject::from_raw(ctx.context().cast()) };
|
|
let cache_dir = env
|
|
.call_method(ctx, "getFilesDir", "()Ljava/io/File;", &[])?
|
|
.l()?;
|
|
let cache_dir: jni::objects::JString = env
|
|
.call_method(&cache_dir, "toString", "()Ljava/lang/String;", &[])?
|
|
.l()?
|
|
.try_into()?;
|
|
let cache_dir = env.get_string(&cache_dir)?;
|
|
let cache_dir = cache_dir.to_str()?;
|
|
Ok(std::path::PathBuf::from(cache_dir).join("config.json"))
|
|
}
|
|
|
|
fn load_config_map(config_path: &std::path::PathBuf) -> HashMap<String, serde_json::Value> {
|
|
match std::fs::read_to_string(config_path) {
|
|
Ok(contents) => serde_json::from_str(&contents).unwrap_or_default(),
|
|
Err(_) => HashMap::new(),
|
|
}
|
|
}
|
|
|
|
fn save_config_map(config: &HashMap<String, serde_json::Value>) -> color_eyre::Result<()> {
|
|
let config_path = get_config_path().expect("Could not get config file path.");
|
|
if let Some(parent) = config_path.parent() {
|
|
info!("Creating config directory: {}", parent.display());
|
|
std::fs::create_dir_all(parent)?;
|
|
}
|
|
let contents = serde_json::to_string_pretty(config)?;
|
|
info!("Writing config to {}", config_path.display());
|
|
std::fs::write(&config_path, contents)?;
|
|
Ok(())
|
|
}
|
|
|
|
fn config_get_default(key: &str) -> Option<serde_json::Value> {
|
|
let default_config = platform_default_config();
|
|
default_config
|
|
.get(key)
|
|
.cloned()
|
|
.or(super::global_default_config().get(key).cloned())
|
|
}
|
|
|
|
fn platform_default_config() -> HashMap<String, serde_json::Value> {
|
|
serde_json::json!({})
|
|
.as_object()
|
|
.unwrap()
|
|
.clone()
|
|
.into_iter()
|
|
.collect()
|
|
}
|