use crate::config::{Config, Plugin};
use anyhow::{bail, Context};
use ron::extensions::Extensions;
use ron::Options;
use std::ffi::OsStr;
use std::mem;
use std::path::Path;
use tracing::{span, Level};
pub fn load_config(config_path: &Path) -> anyhow::Result<Config> {
let _span = span!(Level::TRACE, "load_config").entered();
if !config_path.exists() {
bail!("Config file does not exist, create it using `hyprshell config generate`");
}
let config = match config_path.extension().and_then(OsStr::to_str) {
None | Some("ron") => {
let options = Options::default()
.with_default_extension(Extensions::IMPLICIT_SOME)
.with_default_extension(Extensions::UNWRAP_NEWTYPES)
.with_default_extension(Extensions::UNWRAP_VARIANT_NEWTYPES);
let file = std::fs::File::open(config_path)
.with_context(|| format!("Failed to open config at ({config_path:?})"))?;
options
.from_reader(file)
.context("Failed to read ron config")?
}
Some("json") => {
let file = std::fs::File::open(config_path)
.with_context(|| format!("Failed to open config at ({config_path:?})"))?;
serde_json::from_reader(file).context("Failed to read json config")?
}
#[cfg(feature = "toml_config")]
Some("toml") => {
use std::io::Read;
let mut file = std::fs::File::open(config_path)
.with_context(|| format!("Failed to open config at ({config_path:?})"))?;
let mut content = String::new();
file.read_to_string(&mut content)
.context("Failed to read toml config")?;
toml::from_str(&content).context("Failed to parse toml config")?
}
Some(ext) => bail!("Invalid config file extension: {} (check `FEATURES: ` debug log to see enabled extensions)", ext),
};
check(&config)?;
Ok(config)
}
fn check(config: &Config) -> anyhow::Result<()> {
if config
.windows
.as_ref()
.map(|w| w.scale >= 15f64 || w.scale <= 0f64)
.unwrap_or(false)
{
bail!("Scale factor must be less than 15 and greater than 0");
}
if let Some(l) = &config.launcher {
let mut active_plugins: Vec<&Plugin> = vec![];
for plugin in &l.plugins {
if active_plugins
.iter()
.any(|p| mem::discriminant(*p) == mem::discriminant(plugin))
{
bail!("Duplicate plugin: {:?}", plugin);
} else {
active_plugins.push(plugin);
}
if let Plugin::WebSearch(config) = plugin {
let mut used: Vec<&str> = vec![];
for engine in config {
if used.contains(&engine.key.as_str()) {
bail!("Duplicate search engine key: {}", engine.key);
} else {
used.push(&engine.key);
}
}
}
if let Plugin::Calc() = plugin {
#[cfg(not(feature = "calc"))]
{
bail!(
"Calc Plugin enabled but not compiled in, please enable the calc feature"
);
}
}
}
};
Ok(())
}