git_helpe_rs/
commit.rs

1use core::panic;
2use std::process::Command;
3
4use anyhow::{Error, Result};
5
6use crate::cli::DryRunAndCopyFlag;
7use crate::run_mode::get_run_mode_from_options;
8use crate::run_mode::run_copy;
9use crate::run_mode::RunMode;
10use crate::template::interpolate_on_custom_val;
11use crate::template::validate_interpolation_places_on_custom_pattern;
12use crate::{
13    cli::CommitOperationArguments,
14    git_config::GitConfig,
15    template::{interpolate, validate_interpolation_places_count},
16};
17
18pub fn commit_with_formatted_message(
19    options: CommitOperationArguments,
20    config: GitConfig,
21) -> Result<(), Error> {
22    let selected_commit_format = options.use_template.key;
23
24    let _use_autocomplete_values = &options.use_template.use_autocomplete;
25    let _autocomplete_values = &config.data.autocomplete_values;
26
27    let picked_commit_format = config
28        .data
29        .commit_template_variants
30        .get(&selected_commit_format)
31        .unwrap_or_else(|| {
32            panic!(
33                "No commit template under given key {} \n \
34                You should add it prior to trying to use",
35                selected_commit_format
36            )
37        });
38
39    let is_valid = validate_interpolation_places_count(
40        picked_commit_format,
41        options.use_template.interpolate_values.len(),
42    );
43
44    if is_valid.is_err() {
45        let err: Error = is_valid.err().unwrap();
46        return Err(err);
47    }
48
49    let interpolated_commit = interpolate(
50        picked_commit_format,
51        options.use_template.interpolate_values,
52    )?;
53
54    let interpolated_commit = if options.flags.use_branch_number {
55        let branch_output = Command::new("git").arg("status").output().unwrap().stdout;
56
57        let branch_number = get_branch_number_from_git_status_output(branch_output).unwrap();
58
59        let branch_number_as_interpolate_value = vec![branch_number];
60
61        validate_interpolation_places_on_custom_pattern(
62            &interpolated_commit,
63            branch_number_as_interpolate_value.len(),
64            "{b}",
65        )
66        .unwrap();
67
68        interpolate_on_custom_val(
69            &interpolated_commit,
70            branch_number_as_interpolate_value,
71            "{b}",
72        )
73        .unwrap()
74    } else {
75        interpolated_commit
76    };
77
78    let run_mode = get_run_mode_from_options(DryRunAndCopyFlag {
79        dry_run: options.flags.dry_run,
80        copy: options.flags.copy,
81    });
82
83    return match run_mode {
84        RunMode::Normal => {
85            let cmd = Command::new("git")
86                .arg("commit")
87                .arg("-m")
88                .arg(interpolated_commit)
89                .output()
90                .unwrap()
91                .stdout;
92            println!("{}", String::from_utf8_lossy(&cmd));
93
94            Ok(())
95        }
96        RunMode::DryRun => {
97            println!(
98                "Going to run: \n \
99        git commit -m \"{}\"",
100                interpolated_commit
101            );
102            Ok(())
103        }
104        RunMode::DryRunAndCopy => {
105            let copy_command = config.data.clipboard_commands.copy;
106            println!(
107                "Going to run: \n \
108        echo 'git commit -m \"{}\"' > {}",
109                interpolated_commit, copy_command
110            );
111            Ok(())
112        }
113        RunMode::Copy => run_copy(
114            &config,
115            format!("git commit -m \"{}\"", interpolated_commit),
116        ),
117    };
118}
119
120fn get_branch_number_from_git_status_output(git_status_output: Vec<u8>) -> Result<String, Error> {
121    let higher_bound_of_digits_in_utf8 = &58;
122    let lower_bound_of_digits_in_utf8 = &47;
123    let line_with_branch_name: Vec<u8> = String::from_utf8(git_status_output)
124        .unwrap()
125        .split("\n")
126        .collect::<Vec<&str>>()[0]
127        .into();
128
129    let branch_number: Vec<u8> = line_with_branch_name
130        .into_iter()
131        .filter(|u8_char| {
132            u8_char < higher_bound_of_digits_in_utf8 && u8_char > lower_bound_of_digits_in_utf8
133        })
134        .collect();
135    let branch_number: String = String::from_utf8_lossy(&branch_number).to_string();
136    if branch_number == "" {
137        return Err(Error::msg("There is no number in branch name"));
138    }
139
140    Ok(branch_number)
141}
142
143#[cfg(test)]
144mod tests {
145    use super::*;
146
147    #[test]
148    fn get_branch_number_from_git_status_output_when_no_numbers_in_branch() {
149        let git_output: Vec<u8> = vec![
150            79, 110, 32, 98, 114, 97, 110, 99, 104, 32, 109, 97, 105, 110, 10, 89, 111, 117, 114,
151            32, 98, 114, 97, 110, 99, 104, 32, 105, 115, 32, 117, 112, 32, 116, 111, 32, 100, 97,
152            116, 101, 32, 119, 105, 116, 104, 32, 39, 111, 114, 105, 103, 105, 110, 47, 109, 97,
153            105, 110, 39, 46, 10, 10, 67, 104, 97, 110, 103, 101, 115, 32, 110, 111, 116, 32, 115,
154            116, 97, 103, 101, 100, 32, 102, 111, 114, 32, 99, 111, 109, 109, 105, 116, 58, 10, 32,
155            32, 40, 117, 115, 101, 32, 34, 103, 105, 116, 32, 97, 100, 100, 32, 60, 102, 105, 108,
156            101, 62, 46, 46, 46, 34, 32, 116, 111, 32, 117, 112, 100, 97, 116, 101, 32, 119, 104,
157            97, 116, 32, 119, 105, 108, 108, 32, 98, 101, 32, 99, 111, 109, 109, 105, 116, 116,
158            101, 100, 41, 10, 32, 32, 40, 117, 115, 101, 32, 34, 103, 105, 116, 32, 114, 101, 115,
159            116, 111, 114, 101, 32, 60, 102, 105, 108, 101, 62, 46, 46, 46, 34, 32, 116, 111, 32,
160            100, 105, 115, 99, 97, 114, 100, 32, 99, 104, 97, 110, 103, 101, 115, 32, 105, 110, 32,
161            119, 111, 114, 107, 105, 110, 103, 32, 100, 105, 114, 101, 99, 116, 111, 114, 121, 41,
162            10, 9, 109, 111, 100, 105, 102, 105, 101, 100, 58, 32, 32, 32, 115, 114, 99, 47, 98,
163            114, 97, 110, 99, 104, 46, 114, 115, 10, 9, 109, 111, 100, 105, 102, 105, 101, 100, 58,
164            32, 32, 32, 115, 114, 99, 47, 99, 111, 109, 109, 105, 116, 46, 114, 115, 10, 9, 109,
165            111, 100, 105, 102, 105, 101, 100, 58, 32, 32, 32, 115, 114, 99, 47, 114, 117, 110, 95,
166            109, 111, 100, 101, 46, 114, 115, 10, 10, 110, 111, 32, 99, 104, 97, 110, 103, 101,
167            115, 32, 97, 100, 100, 101, 100, 32, 116, 111, 32, 99, 111, 109, 109, 105, 116, 32, 40,
168            117, 115, 101, 32, 34, 103, 105, 116, 32, 97, 100, 100, 34, 32, 97, 110, 100, 47, 111,
169            114, 32, 34, 103, 105, 116, 32, 99, 111, 109, 109, 105, 116, 32, 45, 97, 34, 41, 10,
170        ];
171
172        let output = get_branch_number_from_git_status_output(git_output);
173        match output {
174            Ok(_) => assert!(false),
175            Err(_) => assert!(true),
176        }
177    }
178    #[test]
179    fn get_branch_number_from_git_status_output_when_no_numbers_beside_branch() {
180        let output: Vec<u8> = vec![
181            79, 110, 32, 98, 114, 97, 110, 99, 104, 32, 102, 101, 97, 116, 117, 114, 101, 47, 49,
182            50, 51, 52, 47, 115, 111, 109, 101, 116, 104, 105, 110, 103, 45, 100, 105, 102, 102,
183            101, 114, 101, 110, 116, 10, 67, 104, 97, 110, 103, 101, 115, 32, 110, 111, 116, 32,
184            115, 116, 97, 103, 101, 100, 32, 102, 111, 114, 32, 99, 111, 109, 109, 105, 116, 58,
185            10, 32, 32, 40, 117, 115, 101, 32, 34, 103, 105, 116, 32, 97, 100, 100, 32, 60, 102,
186            105, 108, 101, 62, 46, 46, 46, 34, 32, 116, 111, 32, 117, 112, 100, 97, 116, 101, 32,
187            119, 104, 97, 116, 32, 119, 105, 108, 108, 32, 98, 101, 32, 99, 111, 109, 109, 105,
188            116, 116, 101, 100, 41, 10, 32, 32, 40, 117, 115, 101, 32, 34, 103, 105, 116, 32, 114,
189            101, 115, 116, 111, 114, 101, 32, 60, 102, 105, 108, 101, 62, 46, 46, 46, 34, 32, 116,
190            111, 32, 100, 105, 115, 99, 97, 114, 100, 32, 99, 104, 97, 110, 103, 101, 115, 32, 105,
191            110, 32, 119, 111, 114, 107, 105, 110, 103, 32, 100, 105, 114, 101, 99, 116, 111, 114,
192            121, 41, 10, 9, 109, 111, 100, 105, 102, 105, 101, 100, 58, 32, 32, 32, 115, 114, 99,
193            47, 98, 114, 97, 110, 99, 104, 46, 114, 115, 10, 9, 109, 111, 100, 105, 102, 105, 101,
194            100, 58, 32, 32, 32, 115, 114, 99, 47, 99, 111, 109, 109, 105, 116, 46, 114, 115, 10,
195            9, 109, 111, 100, 105, 102, 105, 101, 100, 58, 32, 32, 32, 115, 114, 99, 47, 114, 117,
196            110, 95, 109, 111, 100, 101, 46, 114, 115, 10, 10, 110, 111, 32, 99, 104, 97, 110, 103,
197            101, 115, 32, 97, 100, 100, 101, 100, 32, 116, 111, 32, 99, 111, 109, 109, 105, 116,
198            32, 40, 117, 115, 101, 32, 34, 103, 105, 116, 32, 97, 100, 100, 34, 32, 97, 110, 100,
199            47, 111, 114, 32, 34, 103, 105, 116, 32, 99, 111, 109, 109, 105, 116, 32, 45, 97, 34,
200            41, 10,
201        ];
202        let output = get_branch_number_from_git_status_output(output).unwrap();
203        assert_eq!(output, "1234");
204    }
205    #[test]
206    fn get_branch_number_from_git_status_output_when_numbers_in_files_in_status() {
207        let output: Vec<u8> = vec![
208            79, 110, 32, 98, 114, 97, 110, 99, 104, 32, 102, 101, 97, 116, 117, 114, 101, 47, 49,
209            50, 51, 52, 47, 115, 111, 109, 101, 116, 104, 105, 110, 103, 45, 100, 105, 102, 102,
210            101, 114, 101, 110, 116, 10, 67, 104, 97, 110, 103, 101, 115, 32, 110, 111, 116, 32,
211            115, 116, 97, 103, 101, 100, 32, 102, 111, 114, 32, 99, 111, 109, 109, 105, 116, 58,
212            10, 32, 32, 40, 117, 115, 101, 32, 34, 103, 105, 116, 32, 97, 100, 100, 32, 60, 102,
213            105, 108, 101, 62, 46, 46, 46, 34, 32, 116, 111, 32, 117, 112, 100, 97, 116, 101, 32,
214            119, 104, 97, 116, 32, 119, 105, 108, 108, 32, 98, 101, 32, 99, 111, 109, 109, 105,
215            116, 116, 101, 100, 41, 10, 32, 32, 40, 117, 115, 101, 32, 34, 103, 105, 116, 32, 114,
216            101, 115, 116, 111, 114, 101, 32, 60, 102, 105, 108, 101, 62, 46, 46, 46, 34, 32, 116,
217            111, 32, 100, 105, 115, 99, 97, 114, 100, 32, 99, 104, 97, 110, 103, 101, 115, 32, 105,
218            110, 32, 119, 111, 114, 107, 105, 110, 103, 32, 100, 105, 114, 101, 99, 116, 111, 114,
219            121, 41, 10, 9, 109, 111, 100, 105, 102, 105, 101, 100, 58, 32, 32, 32, 115, 114, 99,
220            47, 98, 114, 97, 110, 99, 104, 46, 114, 115, 10, 9, 109, 111, 100, 105, 102, 105, 101,
221            100, 58, 32, 32, 32, 115, 114, 99, 47, 99, 111, 109, 109, 105, 116, 46, 114, 115, 10,
222            9, 109, 111, 100, 105, 102, 105, 101, 100, 58, 32, 32, 32, 115, 114, 99, 47, 114, 117,
223            110, 95, 109, 111, 100, 101, 46, 114, 115, 10, 10, 85, 110, 116, 114, 97, 99, 107, 101,
224            100, 32, 102, 105, 108, 101, 115, 58, 10, 32, 32, 40, 117, 115, 101, 32, 34, 103, 105,
225            116, 32, 97, 100, 100, 32, 60, 102, 105, 108, 101, 62, 46, 46, 46, 34, 32, 116, 111,
226            32, 105, 110, 99, 108, 117, 100, 101, 32, 105, 110, 32, 119, 104, 97, 116, 32, 119,
227            105, 108, 108, 32, 98, 101, 32, 99, 111, 109, 109, 105, 116, 116, 101, 100, 41, 10, 9,
228            115, 111, 109, 101, 95, 102, 105, 108, 101, 95, 49, 50, 51, 46, 116, 120, 116, 10, 9,
229            115, 111, 109, 101, 95, 102, 105, 108, 101, 95, 55, 56, 57, 46, 116, 120, 116, 10, 10,
230            110, 111, 32, 99, 104, 97, 110, 103, 101, 115, 32, 97, 100, 100, 101, 100, 32, 116,
231            111, 32, 99, 111, 109, 109, 105, 116, 32, 40, 117, 115, 101, 32, 34, 103, 105, 116, 32,
232            97, 100, 100, 34, 32, 97, 110, 100, 47, 111, 114, 32, 34, 103, 105, 116, 32, 99, 111,
233            109, 109, 105, 116, 32, 45, 97, 34, 41, 10,
234        ];
235        let output = get_branch_number_from_git_status_output(output).unwrap();
236        assert_eq!(output, "1234")
237    }
238    #[test]
239    fn get_branch_number_from_git_status_output_when_numbers_only_in_files_in_status() {
240        let output: Vec<u8> = vec![
241            79, 110, 32, 98, 114, 97, 110, 99, 104, 32, 109, 97, 105, 110, 10, 89, 111, 117, 114,
242            32, 98, 114, 97, 110, 99, 104, 32, 105, 115, 32, 117, 112, 32, 116, 111, 32, 100, 97,
243            116, 101, 32, 119, 105, 116, 104, 32, 39, 111, 114, 105, 103, 105, 110, 47, 109, 97,
244            105, 110, 39, 46, 10, 10, 67, 104, 97, 110, 103, 101, 115, 32, 110, 111, 116, 32, 115,
245            116, 97, 103, 101, 100, 32, 102, 111, 114, 32, 99, 111, 109, 109, 105, 116, 58, 10, 32,
246            32, 40, 117, 115, 101, 32, 34, 103, 105, 116, 32, 97, 100, 100, 32, 60, 102, 105, 108,
247            101, 62, 46, 46, 46, 34, 32, 116, 111, 32, 117, 112, 100, 97, 116, 101, 32, 119, 104,
248            97, 116, 32, 119, 105, 108, 108, 32, 98, 101, 32, 99, 111, 109, 109, 105, 116, 116,
249            101, 100, 41, 10, 32, 32, 40, 117, 115, 101, 32, 34, 103, 105, 116, 32, 114, 101, 115,
250            116, 111, 114, 101, 32, 60, 102, 105, 108, 101, 62, 46, 46, 46, 34, 32, 116, 111, 32,
251            100, 105, 115, 99, 97, 114, 100, 32, 99, 104, 97, 110, 103, 101, 115, 32, 105, 110, 32,
252            119, 111, 114, 107, 105, 110, 103, 32, 100, 105, 114, 101, 99, 116, 111, 114, 121, 41,
253            10, 9, 109, 111, 100, 105, 102, 105, 101, 100, 58, 32, 32, 32, 115, 114, 99, 47, 98,
254            114, 97, 110, 99, 104, 46, 114, 115, 10, 9, 109, 111, 100, 105, 102, 105, 101, 100, 58,
255            32, 32, 32, 115, 114, 99, 47, 99, 111, 109, 109, 105, 116, 46, 114, 115, 10, 9, 109,
256            111, 100, 105, 102, 105, 101, 100, 58, 32, 32, 32, 115, 114, 99, 47, 114, 117, 110, 95,
257            109, 111, 100, 101, 46, 114, 115, 10, 10, 85, 110, 116, 114, 97, 99, 107, 101, 100, 32,
258            102, 105, 108, 101, 115, 58, 10, 32, 32, 40, 117, 115, 101, 32, 34, 103, 105, 116, 32,
259            97, 100, 100, 32, 60, 102, 105, 108, 101, 62, 46, 46, 46, 34, 32, 116, 111, 32, 105,
260            110, 99, 108, 117, 100, 101, 32, 105, 110, 32, 119, 104, 97, 116, 32, 119, 105, 108,
261            108, 32, 98, 101, 32, 99, 111, 109, 109, 105, 116, 116, 101, 100, 41, 10, 9, 115, 111,
262            109, 101, 95, 102, 105, 108, 101, 95, 49, 50, 51, 46, 116, 120, 116, 10, 9, 115, 111,
263            109, 101, 95, 102, 105, 108, 101, 95, 55, 56, 57, 46, 116, 120, 116, 10, 10, 110, 111,
264            32, 99, 104, 97, 110, 103, 101, 115, 32, 97, 100, 100, 101, 100, 32, 116, 111, 32, 99,
265            111, 109, 109, 105, 116, 32, 40, 117, 115, 101, 32, 34, 103, 105, 116, 32, 97, 100,
266            100, 34, 32, 97, 110, 100, 47, 111, 114, 32, 34, 103, 105, 116, 32, 99, 111, 109, 109,
267            105, 116, 32, 45, 97, 34, 41, 10,
268        ];
269        let output = get_branch_number_from_git_status_output(output);
270        match output {
271            Ok(_) => assert!(false),
272            Err(_) => assert!(true),
273        }
274    }
275}