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}