1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
use crate::error::Result;
use regex::{
Regex,
RegexBuilder,
};
use std::ffi::OsStr;
use std::fs;
use std::path::Path;
const CARGO_METADATA_REGEX: &str =
r"^\[(?:workspace|package)\.metadata\.git\-cliff\.";
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Config {
#[serde(default)]
pub changelog: ChangelogConfig,
#[serde(default)]
pub git: GitConfig,
}
#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)]
pub struct ChangelogConfig {
pub header: Option<String>,
pub body: Option<String>,
pub footer: Option<String>,
pub trim: Option<bool>,
}
#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)]
pub struct GitConfig {
pub conventional_commits: Option<bool>,
pub filter_unconventional: Option<bool>,
pub split_commits: Option<bool>,
pub commit_preprocessors: Option<Vec<CommitPreprocessor>>,
pub commit_parsers: Option<Vec<CommitParser>>,
pub link_parsers: Option<Vec<LinkParser>>,
pub filter_commits: Option<bool>,
pub tag_pattern: Option<String>,
#[serde(with = "serde_regex", default)]
pub skip_tags: Option<Regex>,
#[serde(with = "serde_regex", default)]
pub ignore_tags: Option<Regex>,
pub date_order: Option<bool>,
pub sort_commits: Option<String>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct CommitParser {
#[serde(with = "serde_regex", default)]
pub message: Option<Regex>,
#[serde(with = "serde_regex", default)]
pub body: Option<Regex>,
pub group: Option<String>,
pub default_scope: Option<String>,
pub scope: Option<String>,
pub skip: Option<bool>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct CommitPreprocessor {
#[serde(with = "serde_regex")]
pub pattern: Regex,
pub replace: Option<String>,
pub replace_command: Option<String>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct LinkParser {
#[serde(with = "serde_regex")]
pub pattern: Regex,
pub href: String,
pub text: Option<String>,
}
impl Config {
pub fn parse(path: &Path) -> Result<Config> {
let config_builder = if path.file_name() == Some(OsStr::new("Cargo.toml")) {
let contents = fs::read_to_string(path)?;
let metadata_regex = RegexBuilder::new(CARGO_METADATA_REGEX)
.multi_line(true)
.build()?;
let contents = metadata_regex.replace_all(&contents, "[");
config::Config::builder().add_source(config::File::from_str(
&contents,
config::FileFormat::Toml,
))
} else {
config::Config::builder().add_source(config::File::from(path))
};
Ok(config_builder
.add_source(config::Environment::with_prefix("CLIFF").separator("_"))
.build()?
.try_deserialize()?)
}
}
#[cfg(test)]
mod test {
use super::*;
use pretty_assertions::assert_eq;
use std::env;
use std::path::PathBuf;
#[test]
fn parse_config() -> Result<()> {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.parent()
.expect("parent directory not found")
.to_path_buf()
.join("config")
.join(crate::DEFAULT_CONFIG);
env::set_var("CLIFF_CHANGELOG_FOOTER", "test");
let config = Config::parse(&path)?;
assert_eq!(Some(String::from("test")), config.changelog.footer);
Ok(())
}
}