1use crate::enums::{Parameter, DASHED};
2use std::collections::HashMap;
3
4#[derive(Default, Clone, Debug)]
6pub struct Command<'a> {
7 pub long_command: String,
8 pub short_command: String,
9 pub description: String,
10 pub parameter_name: Parameter<'a>,
11 pub dash_status: DASHED,
12}
13
14impl<'a> Command<'a> {
15 pub fn new(
16 long_command: String,
17 short_command: String,
18 description: String,
19 parameter_name: Parameter<'a>,
20 dash_status: DASHED,
21 ) -> Self {
22 Self {
23 long_command,
24 short_command,
25 description,
26 parameter_name,
27 dash_status,
28 }
29 }
30}
31
32#[derive(Debug, Default)]
34pub struct HelpMessageBuilder<'a> {
35 commands: Vec<Command<'a>>,
36}
37
38impl<'a> HelpMessageBuilder<'a> {
39 pub fn command(
41 mut self,
42 short_command: &str,
43 long_command: &str,
44 parameter_name: Parameter<'a>,
45 description: &str,
46 dash_status: DASHED,
47 ) -> Self {
48 self.commands.push(Command::new(
49 long_command.to_owned(),
50 short_command.to_owned(),
51 description.to_owned(),
52 parameter_name,
53 dash_status,
54 ));
55 self
56 }
57 fn max_width(commands: &Vec<Command<'a>>) -> HashMap<&'a str, usize> {
58 let mut result = HashMap::new();
59 result.insert("short", 0);
60 result.insert("long", 0);
61 result.insert("description", 0);
62 result.insert("parameter_name", 0);
63
64 for command in commands {
65 if command.short_command.len() > *result.get("short").unwrap() {
66 *result.get_mut("short").unwrap() = command.short_command.len();
67 }
68 if command.long_command.len() > *result.get("long").unwrap() {
69 *result.get_mut("long").unwrap() = command.long_command.len();
70 }
71 if command.description.len() > *result.get("description").unwrap() {
72 *result.get_mut("description").unwrap() = command.description.len();
73 }
74 if command.parameter_name.get_len() > *result.get("parameter_name").unwrap() {
75 *result.get_mut("parameter_name").unwrap() = command.parameter_name.get_len();
76 }
77 }
78 *result.get_mut("short").unwrap() += 1;
79 *result.get_mut("long").unwrap() += 2;
80 *result.get_mut("parameter_name").unwrap() += 2;
81
82 result
83 }
84
85 fn field_wrapper(
86 max_widths: &HashMap<&str, usize>,
87 field: &str,
88 max_character_number: usize,
89 ) -> String {
90 let mut result = String::with_capacity(field.len() + max_character_number * 3);
91 let mut line_limit = max_character_number;
92 let mut previous_index = 0;
93 let mut current_index = line_limit;
94 let spaces = max_widths["short"] + max_widths["long"] + max_widths["parameter_name"] + 8;
95 if line_limit >= field.len() {
96 result.push_str(field);
97 }
98 'outer: while line_limit < field.len() {
99 while current_index >= previous_index {
100 if current_index >= field.len() {
101 result.push_str(&format!(
102 "{:<spaces$}{}",
103 " ",
104 &field[previous_index..field.len()].trim()
105 ));
106 break 'outer;
107 }
108 if &field[current_index..current_index + 1] == " " {
109 if previous_index == 0 {
110 result.push_str(&format!(
111 "{}\n",
112 &field[previous_index..current_index].trim()
113 ));
114 } else {
115 result.push_str(&format!(
116 "{:<spaces$}{}\n",
117 " ",
118 &field[previous_index..current_index].trim()
119 ));
120 }
121 previous_index = current_index;
122 line_limit = current_index + max_character_number;
123 current_index = line_limit;
124 } else {
125 current_index -= 1;
126 }
127 if current_index == previous_index {
128 if previous_index == 0 {
129 result.push_str(&format!(
130 "{}-\n",
131 &field[current_index..current_index + max_character_number].trim()
132 ));
133 } else {
134 result.push_str(&format!(
135 "{:<spaces$}{}-\n",
136 " ",
137 &field[current_index..current_index + max_character_number].trim()
138 ));
139 }
140 current_index += max_character_number * 2;
141 line_limit = current_index;
142 previous_index = current_index - max_character_number;
143 if line_limit >= field.len() {
144 result.push_str(&format!(
145 "{:<spaces$}{}",
146 " ",
147 &field[previous_index..field.len()].trim()
148 ));
149 break 'outer;
150 }
151 }
152 }
153 }
154 result
155 }
156
157 fn craft(command: Command<'a>, max_widths: &HashMap<&str, usize>) -> String {
158 let mut message = String::with_capacity(200);
159 let parameter = match command.parameter_name {
160 Parameter::OPTIONAL(param) => format!("[{}]", param),
161 Parameter::REQUIRED(param) => format!("<{}>", param),
162 Parameter::NO => String::new(),
163 };
164
165 let description_str = Self::field_wrapper(max_widths, &command.description, 40);
166
167 message.push_str(&format!(
168 "{:<short$} {:<long$} {:<parameter_name$} {:<description$}\n",
169 command.short_command,
170 command.long_command,
171 parameter,
172 description_str,
173 short = max_widths.get("short").unwrap(),
174 long = max_widths.get("long").unwrap(),
175 parameter_name = max_widths.get("parameter_name").unwrap(),
176 description = max_widths.get("description").unwrap(),
177 ));
178
179 message
180 }
181 pub fn build(self) -> String {
183 let commands = self.commands;
184 let max_widths = Self::max_width(&commands);
185 let mut result = String::new();
186
187 result.push_str(&format!(
188 "{:<short$} {:<long$} {:<parameter_name$} {:<description$}\n",
189 "",
190 "",
191 "",
192 "",
193 short = max_widths.get("short").unwrap(),
194 long = max_widths.get("long").unwrap(),
195 parameter_name = max_widths.get("parameter_name").unwrap(),
196 description = max_widths.get("description").unwrap(),
197 ));
198
199 for mut command in commands.clone() {
200 match command.dash_status {
201 DASHED::YES => {
202 if !command.long_command.is_empty() {
203 let mut long = String::with_capacity(command.long_command.len() + 2);
204 long.push_str("--");
205 long.push_str(&command.long_command);
206 command.long_command = long
207 }
208 if !command.short_command.is_empty() {
209 let mut short = String::with_capacity(command.short_command.len() + 1);
210 short.push('-');
211 short.push_str(&command.short_command);
212 command.short_command = short
213 }
214 }
215 DASHED::NO => {
216 command.short_command = format!(" {}", command.short_command);
217 command.long_command = format!(" {}", command.long_command);
218 }
219 };
220
221 result.push_str(&Self::craft(command, &max_widths));
222 }
223 if *max_widths.get("parameter_name").unwrap() > 2 {
224 result.push_str("\n\nNote: <> parameter is required. [] parameter is optional");
225 }
226 result
227 }
228}