1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
use dialoguer::{Confirm, Editor};
use std::fmt::{self, Display, Formatter};

#[derive(Copy, Clone)]
pub struct CommitType {
    pub text: &'static str,
    description: &'static str,
}

impl CommitType {
    pub fn default_commit_types() -> [CommitType; 8] {
        [
            CommitType {
                text: "feat",
                description: "A new feature"
            },
            CommitType {
                text: "fix",
                description: "A bug fix"
            },
            CommitType {
                text: "docs",
                description: "Documentation only changes"
            },
            CommitType {
                text: "style",
                description: "Changes that do not affect the meaning of the code (white-space, fomatting, missing semi-colons, etc)"
            },
            CommitType {
                text: "refactor",
                description: "A code change that neither fixes a bug or adds a feature"
            },
            CommitType {
                text: "perf",
                description: "A code change that improves performance"
            },
            CommitType {
                text: "test",
                description: "Added, modified or removed tests"
            },
            CommitType {
                text: "chore",
                description: "Change to the build process, auxiliary tools, libraries or CI"
            },
        ]
    }
}

impl Display for CommitType {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "{:9}: {}", self.text, self.description)
    }
}

pub fn get_optional_commit_body_and_footer() -> Option<String> {
    let should_open_editor = Confirm::new()
        .with_prompt("Do you want to write a long description?")
        .default(false)
        .show_default(false)
        .interact()
        .unwrap();
    if should_open_editor {
        return Editor::new().edit("").unwrap();
    }
    None
}

pub fn put_together_first_line(commit_type: CommitType, scope: String, subject: String) -> String {
    let mut first_line = commit_type.text.to_string();
    if scope.is_empty() {
        first_line.push_str(": ");
    } else {
        first_line.push_str(&format!("({}): ", scope));
    }
    first_line.push_str(&subject.to_lowercase());

    first_line
}

pub fn put_together_commit_message(
    first_line: String,
    optional_body_and_footer: Option<String>,
) -> String {
    let mut format_commit_message = first_line;
    if let Some(text) = optional_body_and_footer {
        format_commit_message.push_str(&format!("\n\n{}", text));
    }
    format_commit_message
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_commit_to_string() {
        let fix = CommitType {
            text: "fix",
            description: "just for test",
        };
        assert_eq!(fix.to_string(), "fix      : just for test");
    }

    #[test]
    fn test_composite_commit() {
        let bug = CommitType {
            text: "bug",
            description: "a test",
        };
        let scope = String::from("view");
        let subject = String::from("test example");
        let other: Option<String> = None;
        let first_line = put_together_first_line(bug, scope, subject);
        let result = put_together_commit_message(first_line, other);
        assert_eq!(result, String::from("bug(view): test example"))
    }
}