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
use std::{error::Error, process};

use rustyline::{error::ReadlineError::Interrupted, DefaultEditor};

use crate::authors::author::Author;

use self::cli_err::CliError;
mod cli_err;

pub struct FancyCli {
	reader: DefaultEditor,
}

pub trait Cli {
	fn ask_for_commit_message(&mut self) -> Result<String, Box<dyn Error>>;
	fn ask_for_aliases(&mut self, authors: Vec<Author>) -> Result<Vec<String>, Box<dyn Error>>;
	fn ask_for_commit_message_with_pre_populated(&mut self, prev_commit_msg: String) -> Result<String, Box<dyn Error>>;
}

impl Cli for FancyCli {
	fn ask_for_commit_message(&mut self) -> Result<String, Box<dyn Error>> {
		match self.reader.readline("Enter your commit message:\n") {
			Ok(commit_message) => FancyCli::process_commit_msg(commit_message),
			Err(Interrupted) => {
				eprintln!("^C");
				process::exit(1);
			}
			Err(_) => Err(CliError::with("Unexpected error")),
		}
	}

	fn ask_for_commit_message_with_pre_populated(&mut self, prev_commit_msg: String) -> Result<String, Box<dyn Error>> {
		match self
			.reader
			.readline_with_initial("Enter your commit message:\n", (prev_commit_msg.as_str(), ""))
		{
			Ok(commit_message) => FancyCli::process_commit_msg(commit_message),
			Err(_) => Err(CliError::with("Unexpected error")),
		}
	}

	fn ask_for_aliases(&mut self, authors: Vec<Author>) -> Result<Vec<String>, Box<dyn Error>> {
		let printable_authors = authors
			.iter()
			.map(|a| a.to_string())
			.collect::<Vec<String>>()
			.join("\n");

		match self.reader.readline(
			format!(
				"\n{}\n\nEnter co-authors aliases separated by spaces:\n",
				printable_authors
			)
			.as_str(),
		) {
			Ok(aliases) => Ok(FancyCli::process_aliases(aliases)),
			Err(Interrupted) => {
				eprintln!("^C");
				process::exit(1);
			}
			Err(_) => Err(CliError::with("Unexpected error")),
		}
	}
}

impl FancyCli {
	pub fn new() -> Self {
		Self {
			reader: DefaultEditor::new().unwrap(),
		}
	}

	fn process_commit_msg(msg: String) -> Result<String, Box<dyn Error>> {
		let trimmed_msg = msg.trim().to_string();
		FancyCli::validate_commit_msg(trimmed_msg)
	}

	fn validate_commit_msg(msg: String) -> Result<String, Box<dyn Error>> {
		match msg.is_empty() {
			false => Ok(msg),
			true => Err(CliError::with("Commit message cannot be empty.")),
		}
	}

	fn process_aliases(aliases: String) -> Vec<String> {
		aliases.split_whitespace().map(|s| s.to_string()).collect()
	}
}

impl Default for FancyCli {
	fn default() -> Self {
		Self::new()
	}
}

#[cfg(test)]
mod test;