git_conventional/
error.rs

1//! All errors related to Conventional Commits.
2
3use std::fmt;
4
5/// The error returned when parsing a commit fails.
6pub struct Error {
7    kind: ErrorKind,
8
9    context: Option<Box<dyn fmt::Display + Send + Sync>>,
10    commit: Option<String>,
11}
12
13impl Error {
14    /// Create a new error from a `ErrorKind`.
15    pub(crate) fn new(kind: ErrorKind) -> Self {
16        Self {
17            kind,
18            context: None,
19            commit: None,
20        }
21    }
22
23    pub(crate) fn with_nom(
24        commit: &str,
25        err: winnow::error::ParseError<&str, winnow::error::ContextError>,
26    ) -> Self {
27        use winnow::error::StrContext;
28        use ErrorKind::{
29            InvalidBody, InvalidFormat, InvalidScope, MissingDescription, MissingType,
30        };
31
32        let mut kind = InvalidFormat;
33        for context in err.inner().context() {
34            kind = match context {
35                StrContext::Label(string) => match *string {
36                    crate::parser::SUMMARY => MissingType,
37                    crate::parser::TYPE => MissingType,
38                    crate::parser::SCOPE => InvalidScope,
39                    crate::parser::DESCRIPTION => MissingDescription,
40                    crate::parser::BODY => InvalidBody,
41                    _ => kind,
42                },
43                _ => kind,
44            };
45        }
46
47        Self {
48            kind,
49            context: None,
50            commit: Some(commit.to_owned()),
51        }
52    }
53
54    pub(crate) fn set_context(mut self, context: Box<dyn fmt::Display + Send + Sync>) -> Self {
55        self.context = Some(context);
56        self
57    }
58
59    /// The kind of error.
60    pub fn kind(&self) -> ErrorKind {
61        self.kind
62    }
63}
64
65impl fmt::Debug for Error {
66    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67        f.debug_struct("Error")
68            .field("kind", &self.kind)
69            .field("context", &self.context.as_ref().map(|s| s.to_string()))
70            .field("commit", &self.commit)
71            .finish()
72    }
73}
74
75impl fmt::Display for Error {
76    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77        if let Some(context) = self.context.as_ref() {
78            write!(f, "{}: {}", self.kind, context)
79        } else {
80            write!(f, "{}", self.kind)
81        }
82    }
83}
84
85impl std::error::Error for Error {
86    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
87        None
88    }
89}
90
91/// All possible error kinds returned when parsing a conventional commit.
92#[derive(Copy, Clone, Debug, PartialEq, Eq)]
93#[non_exhaustive]
94pub enum ErrorKind {
95    /// The commit type is missing from the commit message.
96    MissingType,
97
98    /// The scope has an invalid format.
99    InvalidScope,
100
101    /// The description of the commit is missing.
102    MissingDescription,
103
104    /// The body of the commit has an invalid format.
105    InvalidBody,
106
107    /// The footer of the commit has an invalid format.
108    InvalidFooter,
109
110    /// Any other part of the commit does not conform to the conventional commit
111    /// spec.
112    InvalidFormat,
113}
114
115impl fmt::Display for ErrorKind {
116    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117        let s = match self {
118            ErrorKind::MissingType => {
119                "Missing type in the commit summary, expected `type: description`"
120            }
121            ErrorKind::InvalidScope => {
122                "Incorrect scope syntax in commit summary, expected `type(scope): description`"
123            }
124            ErrorKind::MissingDescription => {
125                "Missing description in commit summary, expected `type: description`"
126            }
127            ErrorKind::InvalidBody => "Incorrect body syntax",
128            ErrorKind::InvalidFooter => "Incorrect footer syntax",
129            ErrorKind::InvalidFormat => "Incorrect conventional commit format",
130        };
131        f.write_str(s)
132    }
133}