1use serde::{Deserialize, Serialize};
2use std::fmt;
3use std::{fs, path::PathBuf};
4
5use crate::rule::Rules;
6
7const DEFAULT_CONFIG_ROOT: &str = ".";
9
10const DEFAULT_CONFIG_FILE: [&str; 4] = [
14 ".commitlintrc",
15 ".commitlintrc.json",
16 ".commitlintrc.yaml",
17 ".commitlintrc.yml",
18];
19
20#[derive(Clone, Debug, Default, Deserialize, Serialize)]
22#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
23pub struct Config {
24 pub rules: Rules,
26}
27
28impl fmt::Display for Config {
29 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30 let s = serde_yaml::to_string(&self).unwrap();
31 write!(f, "{}", s)
32 }
33}
34
35pub async fn load(path: Option<PathBuf>) -> Result<Config, String> {
37 let config_file = match &path {
38 Some(p) => Some(p.clone()),
39 None => find_config_file(PathBuf::from(DEFAULT_CONFIG_ROOT)),
40 };
41
42 match (config_file, path) {
43 (Some(p), _) => load_config_file(p).await,
45 (None, None) => Ok(Config::default()),
47 (None, Some(p)) => Err(format!("Configuration file not found in {}", p.display())),
49 }
50}
51
52pub fn find_config_file(path: PathBuf) -> Option<PathBuf> {
55 let mut path = path;
56 for file in DEFAULT_CONFIG_FILE.iter() {
57 path.push(file);
58 if path.exists() {
59 return Some(path);
60 }
61 path.pop();
62 }
63
64 None
65}
66
67pub async fn load_config_file(path: PathBuf) -> Result<Config, String> {
69 if !path.exists() {
70 return Err(format!(
71 "Configuration file not found in {}",
72 path.display()
73 ));
74 }
75
76 match path.extension() {
77 Some(ext) => match ext.to_str() {
78 Some("json") => load_json_config_file(path).await,
79 Some("yaml") | Some("yml") => load_yaml_config_file(path).await,
80 _ => load_unknown_config_file(path).await,
81 },
82 None => Err(format!(
83 "Unsupported configuration file format: {}",
84 path.display()
85 )),
86 }
87}
88
89async fn load_json_config_file(path: PathBuf) -> Result<Config, String> {
91 let text = fs::read_to_string(path).unwrap();
92
93 match serde_json::from_str::<Config>(&text) {
94 Ok(config) => Ok(config),
95 Err(err) => Err(format!("Failed to parse configuration file: {}", err)),
96 }
97}
98
99async fn load_yaml_config_file(path: PathBuf) -> Result<Config, String> {
101 let text = fs::read_to_string(path).unwrap();
102
103 match serde_yaml::from_str::<Config>(&text) {
104 Ok(config) => Ok(config),
105 Err(err) => Err(format!("Failed to parse configuration file: {}", err)),
106 }
107}
108
109async fn load_unknown_config_file(path: PathBuf) -> Result<Config, String> {
113 let text = fs::read_to_string(path.clone()).unwrap();
114
115 if let Ok(config) = serde_json::from_str::<Config>(&text) {
116 return Ok(config);
117 }
118
119 if let Ok(config) = serde_yaml::from_str::<Config>(&text) {
120 return Ok(config);
121 }
122
123 Err(format!(
124 "Failed to parse configuration file: {}",
125 path.display()
126 ))
127}