Skip to main content

committed/
no_style.rs

1#[derive(Clone, PartialEq, Eq, Hash)]
2#[non_exhaustive]
3pub struct Commit<'c> {
4    pub raw_subject: &'c str,
5    pub body: Option<&'c str>,
6}
7
8impl<'c> Commit<'c> {
9    pub fn parse(commit: &'c str) -> Result<Self, anyhow::Error> {
10        let (raw_subject, body) = split_parts(commit);
11        let c = Commit { raw_subject, body };
12
13        Ok(c)
14    }
15}
16
17impl crate::style::Style for Commit<'_> {
18    fn subject(&self) -> &str {
19        self.raw_subject
20    }
21
22    fn body(&self) -> Option<&str> {
23        self.body
24    }
25
26    fn type_(&self) -> Option<unicase::UniCase<&str>> {
27        None
28    }
29
30    fn scope(&self) -> Option<unicase::UniCase<&str>> {
31        None
32    }
33}
34
35static SECTION_RE: std::sync::LazyLock<regex::Regex> =
36    std::sync::LazyLock::new(|| regex::Regex::new("\r?\n").unwrap());
37
38fn split_parts(commit: &str) -> (&str, Option<&str>) {
39    let mut sections = SECTION_RE.splitn(commit, 2);
40    let raw_subject = sections.next().expect("Regex should always match");
41    let body = sections.next().map(|s| s.trim()).unwrap_or("");
42    if body.is_empty() {
43        (raw_subject, None)
44    } else {
45        (raw_subject, Some(body))
46    }
47}
48
49#[cfg(test)]
50mod test_split_parts {
51    use super::*;
52
53    #[test]
54    fn subject() {
55        let actual = split_parts("feat(parser): Parse bad greetings");
56        let expected = ("feat(parser): Parse bad greetings", None);
57        assert_eq!(actual, expected);
58    }
59
60    #[test]
61    fn body() {
62        let actual = split_parts(
63            r#"feat(parser): Parse bad greetings
64
65Hello
66World
67
68Foo
69Bar"#,
70        );
71        let expected = (
72            "feat(parser): Parse bad greetings",
73            Some("Hello\nWorld\n\nFoo\nBar"),
74        );
75        assert_eq!(actual, expected);
76    }
77}