use std::{env::current_dir, fs::read_to_string, path::PathBuf};
use etcetera::{choose_base_strategy, BaseStrategy};
use oxvg_lint::Rules;
use oxvg_optimiser::Extends;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Default)]
pub struct Optimise {
#[serde(skip_serializing_if = "Option::is_none")]
pub extends: Option<Extends>,
pub jobs: oxvg_optimiser::Jobs,
#[serde(skip_serializing_if = "Option::is_none")]
pub omit: Option<Vec<String>>,
}
#[derive(Deserialize, Serialize, Debug, Default)]
#[serde(rename_all = "camelCase")]
pub struct Config {
pub optimise: Option<Optimise>,
pub lint: Option<Rules>,
}
impl Config {
fn load_local() -> std::io::Result<(String, PathBuf)> {
let mut path = current_dir()?;
path.push("oxvgrc.json");
Ok((read_to_string(&path)?, path))
}
fn load_base() -> std::io::Result<(String, PathBuf)> {
let mut path = choose_base_strategy()
.unwrap_or_else(|err| panic!("{err}"))
.config_dir();
path.push("oxvg");
path.push("config.json");
Ok((read_to_string(&path)?, path))
}
pub fn load() -> std::io::Result<Self> {
let (file, path) = Self::load_local().or_else(|_| Self::load_base())?;
Ok(serde_json::from_str(&file).unwrap_or_else(|err| {
panic!(
"Configuration at {} cannot be parsed: {err}",
path.to_string_lossy()
)
}))
}
}
impl Optimise {
pub fn resolve_jobs(&self) -> oxvg_optimiser::Jobs {
let Some(extends) = &self.extends else {
return self.jobs.clone();
};
let mut result = extends.extend(&self.jobs);
if let Some(omit) = self.omit.as_ref() {
for omit in omit {
result.omit(omit);
}
}
result
}
}
#[test]
fn serde() -> anyhow::Result<()> {
let config: Optimise = serde_json::from_str(
r#"{
"extends": "default",
"jobs": {
"removeComments": {}
}
}"#,
)?;
assert_eq!(
serde_json::to_string(&config)?,
String::from(r#"{"extends":"default","jobs":{"removeComments":{}}}"#),
"preset should not affect deserialization of `jobs`"
);
Ok(())
}
#[test]
#[allow(clippy::default_trait_access)]
fn resolve_jobs() {
let config = Optimise {
extends: Some(Extends::Default),
jobs: oxvg_optimiser::Jobs {
precheck: Some(Default::default()),
remove_scripts: Some(Default::default()),
..oxvg_optimiser::Jobs::none()
},
omit: Some(vec![
String::from("precheck"),
String::from("remove_doctype"),
]),
};
let resolved = config.resolve_jobs();
assert!(resolved.precheck.is_none(), "omitted value should be None");
assert!(
resolved.remove_doctype.is_none(),
"omitted value should be None"
);
assert!(
resolved.remove_scripts.is_some(),
"specified value should be Some"
);
assert!(
resolved.remove_comments.is_some(),
"extended value should be Some"
);
}