use serde::{Deserialize, Serialize};
use std::fmt;
use std::{fs, path::PathBuf};
use crate::rule::Rules;
const DEFAULT_CONFIG_ROOT: &str = ".";
const DEFAULT_CONFIG_FILE: [&str; 4] = [
".commitlintrc",
".commitlintrc.json",
".commitlintrc.yaml",
".commitlintrc.yml",
];
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct Config {
pub rules: Rules,
}
impl fmt::Display for Config {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = serde_yaml::to_string(&self).unwrap();
write!(f, "{}", s)
}
}
pub async fn load(path: Option<PathBuf>) -> Result<Config, String> {
let config_file = match &path {
Some(p) => Some(p.clone()),
None => find_config_file(PathBuf::from(DEFAULT_CONFIG_ROOT)),
};
match (config_file, path) {
(Some(p), _) => load_config_file(p).await,
(None, None) => Ok(Config::default()),
(None, Some(p)) => Err(format!("Configuration file not found in {}", p.display())),
}
}
pub fn find_config_file(path: PathBuf) -> Option<PathBuf> {
let mut path = path;
for file in DEFAULT_CONFIG_FILE.iter() {
path.push(file);
if path.exists() {
return Some(path);
}
path.pop();
}
None
}
pub async fn load_config_file(path: PathBuf) -> Result<Config, String> {
if !path.exists() {
return Err(format!(
"Configuration file not found in {}",
path.display()
));
}
match path.extension() {
Some(ext) => match ext.to_str() {
Some("json") => load_json_config_file(path).await,
Some("yaml") | Some("yml") => load_yaml_config_file(path).await,
_ => load_unknown_config_file(path).await,
},
None => Err(format!(
"Unsupported configuration file format: {}",
path.display()
)),
}
}
async fn load_json_config_file(path: PathBuf) -> Result<Config, String> {
let text = fs::read_to_string(path).unwrap();
match serde_json::from_str::<Config>(&text) {
Ok(config) => Ok(config),
Err(err) => Err(format!("Failed to parse configuration file: {}", err)),
}
}
async fn load_yaml_config_file(path: PathBuf) -> Result<Config, String> {
let text = fs::read_to_string(path).unwrap();
match serde_yaml::from_str::<Config>(&text) {
Ok(config) => Ok(config),
Err(err) => Err(format!("Failed to parse configuration file: {}", err)),
}
}
async fn load_unknown_config_file(path: PathBuf) -> Result<Config, String> {
let text = fs::read_to_string(path.clone()).unwrap();
if let Ok(config) = serde_json::from_str::<Config>(&text) {
return Ok(config);
}
if let Ok(config) = serde_yaml::from_str::<Config>(&text) {
return Ok(config);
}
Err(format!(
"Failed to parse configuration file: {}",
path.display()
))
}