use anstream::println;
#[derive(Debug, serde::Serialize)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub(crate) struct Message<'s> {
pub(crate) source: Source<'s>,
pub(crate) severity: Severity,
pub(crate) content: Content<'s>,
}
impl<'s> Message<'s> {
pub(crate) fn error<S, C>(source: S, content: C) -> Self
where
S: Into<Source<'s>>,
C: Into<Content<'s>>,
{
Message {
source: source.into(),
severity: Severity::Error,
content: content.into(),
}
}
}
#[derive(Copy, Clone, Debug, serde::Serialize, derive_more::From, derive_more::Display)]
#[serde(rename_all = "snake_case")]
#[serde(untagged)]
#[non_exhaustive]
pub(crate) enum Source<'s> {
#[serde(serialize_with = "serialize_oid")]
Oid(git2::Oid),
ShortId(&'s str),
#[display("{}", _0.display())]
Path(&'s std::path::Path),
}
fn serialize_oid<S>(oid: &git2::Oid, s: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let oid = oid.to_string();
s.serialize_str(&oid)
}
#[derive(Copy, Clone, Debug, serde::Serialize, derive_more::Display)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub(crate) enum Severity {
#[display("error")]
Error,
}
#[derive(Debug, serde::Serialize, derive_more::From, derive_more::Display)]
#[serde(rename_all = "snake_case")]
#[serde(tag = "type")]
#[non_exhaustive]
pub(crate) enum Content<'s> {
EmptyCommit(EmptyCommit),
SubjectTooLong(SubjectTooLong),
LineTooLong(LineTooLong),
CapitalizeSubject(CapitalizeSubject<'s>),
NoPunctuation(NoPunctuation),
Imperative(Imperative<'s>),
Wip(Wip),
Fixup(Fixup),
InvalidCommitFormat(InvalidCommitFormat),
DisallowedCommitType(DisallowedCommitType),
DisallowedCommitScope(DisallowedCommitScope),
MergeCommitDisallowed(MergeCommitDisallowed),
DisallowedAuthor(DisallowedAuthor<'s>),
}
#[derive(Clone, Debug, serde::Serialize)]
#[serde(rename_all = "snake_case")]
#[derive(derive_more::Display)]
#[display(
"Commit subject is too long, {} exceeds the max length of {}",
actual_length,
max_length
)]
pub(crate) struct SubjectTooLong {
pub(crate) max_length: usize,
pub(crate) actual_length: usize,
}
#[derive(Clone, Debug, serde::Serialize)]
#[serde(rename_all = "snake_case")]
#[derive(derive_more::Display)]
#[display(
"Line is too long, {} exceeds the max length of {}",
actual_length,
max_length
)]
pub(crate) struct LineTooLong {
pub(crate) max_length: usize,
pub(crate) actual_length: usize,
}
#[derive(Clone, Debug, serde::Serialize)]
#[serde(rename_all = "snake_case")]
#[derive(derive_more::Display)]
#[display("Subject should be capitalized but found `{}`", first_word)]
pub(crate) struct CapitalizeSubject<'s> {
pub(crate) first_word: &'s str,
}
#[derive(Clone, Debug, serde::Serialize)]
#[serde(rename_all = "snake_case")]
#[derive(derive_more::Display)]
#[display("Subject should not be punctuated but found `{}`", punctuation)]
pub(crate) struct NoPunctuation {
pub(crate) punctuation: char,
}
#[derive(Clone, Debug, serde::Serialize)]
#[serde(rename_all = "snake_case")]
#[derive(derive_more::Display)]
#[display("Subject should be in the imperative mood but found `{}`", first_word)]
pub(crate) struct Imperative<'s> {
pub(crate) first_word: &'s str,
}
#[derive(Clone, Debug, serde::Serialize)]
#[serde(rename_all = "snake_case")]
#[derive(derive_more::Display)]
#[display("Work-in-progress commits must be cleaned up")]
pub(crate) struct Wip {}
#[derive(Clone, Debug, serde::Serialize)]
#[serde(rename_all = "snake_case")]
#[derive(derive_more::Display)]
#[display("Fixup commits must be squashed")]
pub(crate) struct Fixup {}
#[derive(Debug, serde::Serialize)]
#[serde(rename_all = "snake_case")]
#[derive(derive_more::Display)]
#[display("Commit is not in {} format: {}", style, error)]
pub(crate) struct InvalidCommitFormat {
#[serde(serialize_with = "serialize_error")]
pub(crate) error: anyhow::Error,
pub(crate) style: crate::config::Style,
}
fn serialize_error<S>(error: &anyhow::Error, s: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let error = error.to_string();
s.serialize_str(&error)
}
#[derive(Clone, Debug, serde::Serialize)]
#[serde(rename_all = "snake_case")]
#[derive(derive_more::Display)]
#[display("Disallowed type `{}` used, please use one of {:?}", used, allowed)]
pub(crate) struct DisallowedCommitType {
pub(crate) used: String,
pub(crate) allowed: Vec<String>,
}
#[derive(Clone, Debug, serde::Serialize)]
#[serde(rename_all = "snake_case")]
#[derive(derive_more::Display)]
#[display("Disallowed scope `{}` used, please use one of {:?}", used, allowed)]
pub(crate) struct DisallowedCommitScope {
pub(crate) used: String,
pub(crate) allowed: Vec<String>,
}
#[derive(Clone, Debug, serde::Serialize)]
#[serde(rename_all = "snake_case")]
#[derive(derive_more::Display)]
#[display("Merge commits are disallowed")]
pub(crate) struct MergeCommitDisallowed {}
#[derive(Clone, Debug, serde::Serialize)]
#[serde(rename_all = "snake_case")]
#[derive(derive_more::Display)]
#[display("Empty commits are disallowed")]
pub(crate) struct EmptyCommit {}
#[derive(Clone, Debug, serde::Serialize)]
#[serde(rename_all = "snake_case")]
#[derive(derive_more::Display)]
#[display(
"Disallowed author `{}` used, please use one matching `{}`",
used,
allowed
)]
pub(crate) struct DisallowedAuthor<'s> {
pub(crate) used: String,
pub(crate) allowed: &'s str,
}
pub(crate) type Report = fn(msg: Message<'_>);
pub(crate) fn print_silent(_: Message<'_>) {}
pub(crate) fn print_brief(msg: Message<'_>) {
let palette = crate::color::Palette::new();
let severity_style = match msg.severity {
Severity::Error => palette.error,
};
println!(
"{:#}: {:#} {:#}",
palette.source(msg.source),
crate::color::Styled::new(msg.severity, severity_style),
palette.content(msg.content)
);
}
pub(crate) fn print_json(msg: Message<'_>) {
println!("{}", serde_json::to_string(&msg).unwrap());
}