pub(crate) mod cargo_toml;
pub(crate) mod lychee_toml;
pub(crate) mod package_json;
pub(crate) mod pyproject_toml;
use super::Config;
use anyhow::{Context, Result, bail};
use std::fs;
use std::path::{Path, PathBuf};
#[derive(Debug)]
pub(crate) enum ConfigMatch {
Found(Box<Config>),
NotFound,
}
pub(crate) trait ConfigLoader {
fn filename(&self) -> &str;
fn load(&self, contents: &str) -> Result<ConfigMatch>;
}
const LOADERS: [&dyn ConfigLoader; 4] = [
&lychee_toml::LycheeTomlLoader,
&cargo_toml::CargoTomlLoader,
&pyproject_toml::PyprojectTomlLoader,
&package_json::PackageJsonLoader,
];
pub(crate) fn default_config_file() -> Result<Option<Config>> {
for loader in LOADERS {
let path = PathBuf::from(loader.filename());
if path.is_file() {
let contents = fs::read_to_string(&path).unwrap_or_default();
match loader.load(&contents) {
Ok(ConfigMatch::Found(config)) => return Ok(Some(*config)),
Ok(ConfigMatch::NotFound) => (),
Err(e) => {
return Err(e.context(format!(
"Cannot load configuration file: {}",
path.display()
)));
}
}
}
}
Ok(None)
}
pub(crate) fn load_from_file(path: &Path) -> Result<Config> {
let contents = fs::read_to_string(path)
.with_context(|| format!("Failed to read config file {}", path.display()))?;
let filename = path.file_name().and_then(|n| n.to_str());
let loader = if let Some(filename) = filename {
LOADERS
.iter()
.find(|loader| filename == loader.filename())
.copied()
.unwrap_or(&lychee_toml::LycheeTomlLoader)
} else {
&lychee_toml::LycheeTomlLoader
};
match loader
.load(&contents)
.with_context(|| format!("Failed to load config from {}", path.display()))?
{
ConfigMatch::Found(config) => Ok(*config),
ConfigMatch::NotFound => bail!("No valid lychee configuration found in {}", path.display()),
}
}