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
use serde::{Serialize, Deserialize};
use dialoguer::{Confirm, Editor};
use std::fmt::{self, Display, Formatter};
use std::fs::read_to_string;
use std::io::Result;
#[derive(Serialize, Deserialize)]
pub struct CommitType {
text: String,
description: String,
}
impl CommitType {
pub fn new(text: &str, description: &str) -> Self {
Self { text: text.to_string(), description: description.to_string() }
}
}
impl Display for CommitType {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{:9}: {}", self.text, self.description)
}
}
pub fn get_default_commit_types() -> Vec<CommitType> {
vec![
CommitType::new("feat", "A new feature"),
CommitType::new("fix", "A bug fix"),
CommitType::new("docs", "Documentation only changes"),
CommitType::new("style", "Changes that do not affect the meaning of the code(white-space, fomatting, missing semi-colons, etc)"),
CommitType::new("refactor", "A code change that neither fixes a bug or adds a feature"),
CommitType::new("perf", "A code change that improves performance"),
CommitType::new("test", "Adding missing tests"),
CommitType::new("chore", "Change to the build process or auxiliary tools and libraries such as documentation generation"),
]
}
pub fn get_cm_types_from_file() -> Result<Vec<CommitType>> {
let f = read_to_string("commit-format.json")?;
let cm_types: Vec<CommitType> = serde_json::from_str(&f)?;
Ok(cm_types)
}
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_commit_message(
commit_type: &CommitType,
scope: String,
subject: String,
optional_body_and_footer: Option<String>,
) -> String {
let mut format_commit_message = commit_type.text.to_string();
if scope.is_empty() {
format_commit_message.push_str(": ");
} else {
format_commit_message.push_str(&format!("({}): ", scope));
}
format_commit_message.push_str(&subject);
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::new("fix", "just for test");
assert_eq!(fix.to_string(), String::from("fix : just for test"));
}
#[test]
fn test_composite_commit() {
let bug = CommitType::new("bug", "a test");
let scope = String::from("view");
let subject = String::from("test example");
let other: Option<String> = None;
let result = put_together_commit_message(&bug, scope, subject, other);
assert_eq!(result, String::from("bug(view): test example"))
}
}