diff --git a/src/git.rs b/src/git.rs index 136e3e5..e54e26e 100644 --- a/src/git.rs +++ b/src/git.rs @@ -63,6 +63,17 @@ fn extract_commit_messages(input: &str) -> Vec { } /// Parse a commit message and return the subject, body, and footers. +/// +/// Please refer the official documentation for the commit message format. +/// See: https://www.conventionalcommits.org/en/v1.0.0/#summary +/// +/// ``` +/// [optional scope]: <-- Subject +/// +/// [optional body] <-- Body +/// +/// [optional footer(s)] <-- Footer +/// ``` pub fn parse_commit_message( message: &str, ) -> (String, Option, Option>) { @@ -107,7 +118,7 @@ pub fn parse_commit_message( /// Note that exclamation mark is not respected as the existing commitlint /// does not have any rules for it. /// See: https://commitlint.js.org/#/reference-rules -pub fn parse_subject(subject: &str) -> Option<(String, Option, String)> { +pub fn parse_subject(subject: &str) -> (String, Option, String) { let re = regex::Regex::new(r"^(?P\w+)(?:\((?P[^\)]+)\))?(!)?\:\s(?P.+)$") .unwrap(); @@ -116,9 +127,10 @@ pub fn parse_subject(subject: &str) -> Option<(String, Option, String)> let scope = captures.name("scope").map(|m| m.as_str().to_string()); let description = captures.name("description").unwrap().as_str().to_string(); - return Some((r#type, scope, description)); + return (r#type, scope, description); } - None + // Fall back to the description. + ("".to_string(), None, subject.to_string()) } #[cfg(test)] @@ -179,10 +191,7 @@ Name: Keke"; footer.clone().unwrap().get("Link"), Some(&"Hello".to_string()) ); - assert_eq!( - footer.unwrap().get("Name"), - Some(&"Keke".to_string()) - ); + assert_eq!(footer.unwrap().get("Name"), Some(&"Keke".to_string())); } #[test] @@ -190,11 +199,11 @@ Name: Keke"; let input = "feat(cli): add dummy option"; assert_eq!( parse_subject(input), - Some(( + ( "feat".to_string(), Some("cli".to_string()), "add dummy option".to_string() - )) + ) ); } @@ -203,11 +212,11 @@ Name: Keke"; let input = "feat(cli)!: add dummy option"; assert_eq!( parse_subject(input), - Some(( + ( "feat".to_string(), Some("cli".to_string()), "add dummy option".to_string() - )) + ) ); } @@ -216,7 +225,7 @@ Name: Keke"; let input = "feat: add dummy option"; assert_eq!( parse_subject(input), - Some(("feat".to_string(), None, "add dummy option".to_string())) + ("feat".to_string(), None, "add dummy option".to_string()) ); } @@ -225,23 +234,20 @@ Name: Keke"; let input = "feat!: add dummy option"; assert_eq!( parse_subject(input), - Some(("feat".to_string(), None, "add dummy option".to_string())) + ("feat".to_string(), None, "add dummy option".to_string()) ); } #[test] fn test_parse_subject_without_message() { let input = ""; - assert_eq!( - parse_subject(input), - None - ); + assert_eq!(parse_subject(input), ("".to_string(), None, "".to_string())); } - #[test] + #[test] fn test_parse_subject_with_error_message() { let input = "test"; assert_eq!( parse_subject(input), - None + ("".to_string(), None, "test".to_string()) ); } } diff --git a/src/message.rs b/src/message.rs index f8cb621..527ec91 100644 --- a/src/message.rs +++ b/src/message.rs @@ -45,25 +45,15 @@ impl Message { /// Create a new Message. pub fn new(raw: String) -> Self { let (subject, body, footers) = parse_commit_message(&raw); - match parse_subject(&subject) { - Some((r#type, scope, description)) => Self { - body, - description: Some(description), - footers, - raw, - r#type: Some(r#type), - scope, - subject: Some(subject), - }, - None => Self { - raw, - description: None, - r#type: None, - scope: None, - body, - footers, - subject: Some(subject), - }, + let (r#type, scope, description) = parse_subject(&subject); + Self { + body, + description: Some(description), + footers, + raw, + r#type: Some(r#type), + scope, + subject: Some(subject), } } } diff --git a/src/rule/type_empty.rs b/src/rule/type_empty.rs index fd3e84d..2030467 100644 --- a/src/rule/type_empty.rs +++ b/src/rule/type_empty.rs @@ -23,7 +23,7 @@ impl Rule for TypeEmpty { } fn validate(&self, message: &Message) -> Option { - if message.r#type.is_none() { + if message.r#type.is_none() || message.r#type.as_ref().unwrap().is_empty() { return Some(Violation { level: self.level.unwrap_or(Self::LEVEL), message: self.message(message), @@ -79,9 +79,6 @@ mod tests { let violation = rule.validate(&message); assert!(violation.is_some()); assert_eq!(violation.clone().unwrap().level, Level::Error); - assert_eq!( - violation.unwrap().message, - "type is empty".to_string() - ); + assert_eq!(violation.unwrap().message, "type is empty".to_string()); } }