static DEFAULT_TYPES: &[&str] = &[
"fix", "feat", "chore", "docs", "style", "refactor", "perf", "test",
];
#[derive(
Copy, Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize, derive_more::Display,
)]
#[serde(rename_all = "snake_case")]
#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
pub(crate) enum Style {
#[serde(alias = "Conventional")]
Conventional,
#[serde(alias = "None")]
None,
}
#[derive(Clone, Default, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
pub(crate) struct Config {
#[cfg_attr(feature = "unstable-schema", schemars(extend("format" = "regex")))]
pub(crate) ignore_author_re: Option<String>,
pub(crate) subject_length: Option<usize>,
pub(crate) subject_capitalized: Option<bool>,
pub(crate) subject_not_punctuated: Option<bool>,
pub(crate) imperative_subject: Option<bool>,
pub(crate) no_fixup: Option<bool>,
pub(crate) no_wip: Option<bool>,
pub(crate) hard_line_length: Option<usize>,
pub(crate) line_length: Option<usize>,
pub(crate) style: Option<Style>,
pub(crate) allowed_types: Option<Vec<String>>,
pub(crate) allowed_scopes: Option<Vec<String>>,
pub(crate) merge_commit: Option<bool>,
#[cfg_attr(feature = "unstable-schema", schemars(extend("format" = "regex")))]
pub(crate) allowed_author_re: Option<String>,
}
impl Config {
pub(crate) fn from_defaults() -> Self {
let empty = Self::default();
Self {
ignore_author_re: empty.ignore_author_re().map(|s| s.to_owned()),
subject_length: Some(empty.subject_length()),
subject_capitalized: Some(empty.subject_capitalized()),
subject_not_punctuated: Some(empty.subject_not_punctuated()),
imperative_subject: Some(empty.imperative_subject()),
no_fixup: Some(empty.no_fixup()),
no_wip: Some(empty.no_wip()),
hard_line_length: Some(empty.hard_line_length()),
line_length: Some(empty.line_length()),
style: Some(empty.style()),
allowed_types: Some(empty.allowed_types().map(|s| s.to_owned()).collect()),
allowed_scopes: Some(empty.allowed_scopes().map(|s| s.to_owned()).collect()),
merge_commit: Some(empty.merge_commit()),
allowed_author_re: empty.allowed_author_re().map(|s| s.to_owned()),
}
}
pub(crate) fn update(&mut self, source: Self) {
if let Some(source) = source.ignore_author_re {
self.ignore_author_re = Some(source);
}
if let Some(source) = source.subject_length {
self.subject_length = Some(source);
}
if let Some(source) = source.subject_capitalized {
self.subject_capitalized = Some(source);
}
if let Some(source) = source.subject_not_punctuated {
self.subject_not_punctuated = Some(source);
}
if let Some(source) = source.imperative_subject {
self.imperative_subject = Some(source);
}
if let Some(source) = source.no_fixup {
self.no_fixup = Some(source);
}
if let Some(source) = source.no_wip {
self.no_wip = Some(source);
}
if let Some(source) = source.hard_line_length {
self.hard_line_length = Some(source);
}
if let Some(source) = source.line_length {
self.line_length = Some(source);
}
if let Some(source) = source.style {
self.style = Some(source);
}
if let Some(source) = source.allowed_types {
self.allowed_types = Some(source);
}
if let Some(source) = source.allowed_scopes {
self.allowed_scopes = Some(source);
}
if let Some(source) = source.merge_commit {
self.merge_commit = Some(source);
}
if let Some(source) = source.allowed_author_re {
self.allowed_author_re = Some(source);
}
}
pub(crate) fn ignore_author_re(&self) -> Option<&str> {
self.ignore_author_re.as_deref()
}
pub(crate) fn subject_length(&self) -> usize {
self.subject_length.unwrap_or(50)
}
pub(crate) fn subject_capitalized(&self) -> bool {
self.subject_capitalized.unwrap_or(true)
}
pub(crate) fn subject_not_punctuated(&self) -> bool {
self.subject_not_punctuated.unwrap_or(true)
}
pub(crate) fn imperative_subject(&self) -> bool {
self.imperative_subject.unwrap_or(true)
}
pub(crate) fn no_fixup(&self) -> bool {
self.no_fixup.unwrap_or(true)
}
pub(crate) fn no_wip(&self) -> bool {
self.no_wip.unwrap_or(true)
}
pub(crate) fn line_length(&self) -> usize {
self.line_length.unwrap_or(72)
}
pub(crate) fn hard_line_length(&self) -> usize {
self.hard_line_length.unwrap_or(0)
}
pub(crate) fn style(&self) -> Style {
self.style.unwrap_or(Style::None)
}
pub(crate) fn allowed_types<'s>(&'s self) -> Box<dyn Iterator<Item = &'s str> + 's> {
self.allowed_types
.as_ref()
.map(|v| {
let b: Box<dyn Iterator<Item = &str>> = Box::new(v.iter().map(|s| s.as_str()));
b
})
.unwrap_or_else(|| Box::new(DEFAULT_TYPES.iter().copied()))
}
pub(crate) fn allowed_scopes<'s>(&'s self) -> Box<dyn Iterator<Item = &'s str> + 's> {
self.allowed_scopes
.as_ref()
.map(|v| {
let b: Box<dyn Iterator<Item = &str>> = Box::new(v.iter().map(|s| s.as_str()));
b
})
.unwrap_or_else(|| Box::new([].iter().copied()))
}
pub(crate) fn merge_commit(&self) -> bool {
self.merge_commit.unwrap_or(true)
}
pub(crate) fn allowed_author_re(&self) -> Option<&str> {
self.allowed_author_re.as_deref()
}
}
#[cfg(feature = "unstable-schema")]
#[test]
fn dump_schema() {
let schema = schemars::schema_for!(Config);
let dump = serde_json::to_string_pretty(&schema).unwrap();
snapbox::assert_data_eq!(dump, snapbox::file!("../../../config.schema.json").raw());
}