commit_formatter/
lib.rs

1use serde::{Serialize, Deserialize};
2use dialoguer::{Confirm, Editor};
3use std::fmt::{self, Display, Formatter};
4use std::fs::read_to_string;
5use std::io::Result;
6
7#[derive(Serialize, Deserialize)]
8pub struct CommitType {
9    text: String,
10    description: String,
11}
12
13impl CommitType {
14    pub fn new(text: &str, description: &str) -> Self {
15        Self { text: text.to_string(), description: description.to_string() }
16    }
17}
18
19impl Display for CommitType {
20    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
21        write!(f, "{:9}: {}", self.text, self.description)
22    }
23}
24
25pub fn get_default_commit_types() -> Vec<CommitType> {
26    vec![
27        CommitType::new("feat", "A new feature"),
28        CommitType::new("fix", "A bug fix"),
29        CommitType::new("docs", "Documentation only changes"),
30        CommitType::new("style", "Changes that do not affect the meaning of the code(white-space, fomatting, missing semi-colons, etc)"),
31        CommitType::new("refactor", "A code change that neither fixes a bug or adds a feature"),
32        CommitType::new("perf", "A code change that improves performance"),
33        CommitType::new("test", "Adding missing tests"),
34        CommitType::new("chore", "Change to the build process or auxiliary tools and libraries such as documentation generation"),
35    ]
36}
37
38pub fn get_cm_types_from_file() -> Result<Vec<CommitType>> {
39    let f = read_to_string("commit-format.json")?;
40    let cm_types: Vec<CommitType> = serde_json::from_str(&f)?;
41    Ok(cm_types)
42}
43
44pub fn get_optional_commit_body_and_footer() -> Option<String> {
45    let should_open_editor = Confirm::new()
46        .with_prompt("Do you want to write a long description?")
47        .default(false)
48        .show_default(false)
49        .interact()
50        .unwrap();
51    if should_open_editor {
52        return Editor::new().edit("").unwrap();
53    }
54    None
55}
56
57pub fn put_together_commit_message(
58    commit_type: &CommitType,
59    scope: String,
60    subject: String,
61    optional_body_and_footer: Option<String>,
62) -> String {
63    let mut format_commit_message = commit_type.text.to_string();
64    if scope.is_empty() {
65        format_commit_message.push_str(": ");
66    } else {
67        format_commit_message.push_str(&format!("({}): ", scope));
68    }
69    format_commit_message.push_str(&subject);
70    if let Some(text) = optional_body_and_footer {
71        format_commit_message.push_str(&format!("\n\n{}", text));
72    }
73    format_commit_message
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79
80    #[test]
81    fn test_commit_to_string() {
82        let fix = CommitType::new("fix", "just for test");
83        assert_eq!(fix.to_string(), String::from("fix      : just for test"));
84    }
85
86    #[test]
87    fn test_composite_commit() {
88        let bug = CommitType::new("bug", "a test");
89        let scope = String::from("view");
90        let subject = String::from("test example");
91        let other: Option<String> = None;
92        let result = put_together_commit_message(&bug, scope, subject, other);
93        assert_eq!(result, String::from("bug(view): test example"))
94    }
95}