flagon-io/hello

public

Greets people from the command line. The sample repository for g1t.

hello/src/lib.rs

356 lines10,029 bytesCodeBlame

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Greet people from the command line1//! Greetings.
2
Merge #53: Add a --lang option (en, es, fr) to the greeter3/// A supported greeting language.
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
5pub enum Lang {
6 #[default]
7 En,
8 Es,
9 Fr,
10}
11
12impl Lang {
13 /// Parses a language code (`en`, `es` or `fr`).
14 pub fn parse(code: &str) -> Option<Lang> {
15 match code {
16 "en" => Some(Lang::En),
17 "es" => Some(Lang::Es),
18 "fr" => Some(Lang::Fr),
19 _ => None,
20 }
21 }
22}
23
Extract argument parsing into parse_args in lib.rs24/// Parsed command-line options.
Add --times N flag that prints the greeting N times25#[derive(Debug, PartialEq, Eq)]
Extract argument parsing into parse_args in lib.rs26pub struct Options {
27 /// The name to greet, if one was given.
28 pub name: Option<String>,
Add --shout flag that upper-cases the greeting29 /// Whether to upper-case the greeting (`--shout`).
30 pub shout: bool,
Add --version flag that prints the crate version and exits31 /// Whether to print the crate version and exit (`--version`).
32 pub version: bool,
Add --times N flag that prints the greeting N times33 /// How many times to print the greeting (`--times N`).
34 pub times: usize,
Merge #53: Add a --lang option (en, es, fr) to the greeter35 /// The greeting language (`--lang CODE`).
36 pub lang: Lang,
Add --quiet flag that prints nothing37 /// Whether to print nothing (`--quiet`).
38 pub quiet: bool,
Add --loud flag that ends the greeting with three exclamation marks39 /// Whether to end the greeting with three exclamation marks (`--loud`).
40 pub loud: bool,
Merge #64: Add a --bye flag41 /// Whether to print a goodbye after the greeting (`--bye`).
42 pub bye: bool,
Add --wave flag that prints o/ after the greeting43 /// Whether to print a wave after the greeting (`--wave`).
44 pub wave: bool,
Add --times N flag that prints the greeting N times45}
46
47impl Default for Options {
48 fn default() -> Self {
49 Options {
50 name: None,
51 shout: false,
52 version: false,
53 times: 1,
Merge #53: Add a --lang option (en, es, fr) to the greeter54 lang: Lang::En,
Add --quiet flag that prints nothing55 quiet: false,
Add --loud flag that ends the greeting with three exclamation marks56 loud: false,
Merge #64: Add a --bye flag57 bye: false,
Add --wave flag that prints o/ after the greeting58 wave: false,
Add --times N flag that prints the greeting N times59 }
60 }
Extract argument parsing into parse_args in lib.rs61}
62
63/// Parses command-line arguments (excluding the program name).
64///
Add --times N flag that prints the greeting N times65/// `--shout` sets [`Options::shout`], `--version` sets
66/// [`Options::version`], and `--times N` sets [`Options::times`] (the
Merge #53: Add a --lang option (en, es, fr) to the greeter67/// next argument is consumed as the value), and `--lang CODE` sets
68/// [`Options::lang`] (likewise consuming the next argument); other
69/// arguments starting with `--` are treated as flags and skipped. The
70/// name is the first remaining argument.
Add --times N flag that prints the greeting N times71///
Merge #53: Add a --lang option (en, es, fr) to the greeter72/// Returns an error message if `--times` has a missing or non-numeric
73/// value, or if `--lang` has a missing or unsupported value.
Add --times N flag that prints the greeting N times74pub fn parse_args<I: IntoIterator<Item = String>>(args: I) -> Result<Options, String> {
Add --shout flag that upper-cases the greeting75 let mut options = Options::default();
Add --times N flag that prints the greeting N times76 let mut args = args.into_iter();
77 while let Some(arg) = args.next() {
Add --shout flag that upper-cases the greeting78 if arg == "--shout" {
79 options.shout = true;
Add --version flag that prints the crate version and exits80 } else if arg == "--version" {
81 options.version = true;
Add --quiet flag that prints nothing82 } else if arg == "--quiet" {
83 options.quiet = true;
Accept -L as a short alias for --loud84 } else if arg == "--loud" || arg == "-L" {
Add --loud flag that ends the greeting with three exclamation marks85 options.loud = true;
Merge #64: Add a --bye flag86 } else if arg == "--bye" {
87 options.bye = true;
Add --wave flag that prints o/ after the greeting88 } else if arg == "--wave" {
89 options.wave = true;
Add --times N flag that prints the greeting N times90 } else if arg == "--times" {
91 let value = args
92 .next()
93 .ok_or_else(|| "--times requires a value".to_owned())?;
94 options.times = value
95 .parse()
96 .map_err(|_| format!("--times value must be a number, got {value:?}"))?;
Merge #53: Add a --lang option (en, es, fr) to the greeter97 } else if arg == "--lang" {
98 let value = args
99 .next()
100 .ok_or_else(|| "--lang requires a value".to_owned())?;
Add CI workflow for formatting, lints and tests101 options.lang = Lang::parse(&value)
102 .ok_or_else(|| format!("--lang value must be one of en, es, fr, got {value:?}"))?;
Add --shout flag that upper-cases the greeting103 } else if !arg.starts_with("--") && options.name.is_none() {
104 options.name = Some(arg);
105 }
106 }
Add --times N flag that prints the greeting N times107 Ok(options)
Extract argument parsing into parse_args in lib.rs108}
109
Add --shout flag that upper-cases the greeting110/// `text` upper-cased.
111pub fn shout(text: &str) -> String {
112 text.to_uppercase()
113}
114
Add --loud flag that ends the greeting with three exclamation marks115/// `text` with its final exclamation mark replaced by three.
116pub fn loud(text: &str) -> String {
117 format!("{}!!!", text.strip_suffix('!').unwrap_or(text))
118}
119
Greet people from the command line120/// The greeting for `name`.
Merge main into wave flag branch121pub fn hail(name: &str) -> String {
Merge #53: Add a --lang option (en, es, fr) to the greeter122 greeting_in(name, Lang::En)
123}
124
125/// The greeting for `name` in `lang`.
126pub fn greeting_in(name: &str, lang: Lang) -> String {
Handle empty and whitespace-only names, trim whitespace from input127 let trimmed = name.trim();
128 let display_name = if trimmed.is_empty() { "world" } else { trimmed };
Merge #53: Add a --lang option (en, es, fr) to the greeter129 match lang {
130 Lang::En => format!("Hi, {display_name}!"),
131 Lang::Es => format!("¡Hola, {display_name}!"),
132 Lang::Fr => format!("Bonjour, {display_name} !"),
133 }
Greet people from the command line134}
135
Add --wave flag that prints o/ after the greeting136/// The wave printed by `--wave`.
137pub fn wave() -> &'static str {
138 "o/"
139}
140
Add farewell function alongside greeting141/// The farewell for `name`.
Merge main into wave flag branch142pub fn part(name: &str) -> String {
Add farewell function alongside greeting143 let trimmed = name.trim();
144 let display_name = if trimmed.is_empty() { "world" } else { trimmed };
145 format!("Goodbye, {display_name}!")
146}
147
Greet people from the command line148#[cfg(test)]
149mod tests {
150 use super::*;
151
Extract argument parsing into parse_args in lib.rs152 fn args(list: &[&str]) -> Vec<String> {
153 list.iter().map(|s| s.to_string()).collect()
154 }
155
156 #[test]
Add --quiet flag that prints nothing157 fn parse_args_quiet() {
158 assert!(parse_args(args(&["--quiet", "Ada"])).unwrap().quiet);
159 assert!(!parse_args(args(&["Ada"])).unwrap().quiet);
160 }
161
162 #[test]
Merge #64: Add a --bye flag163 fn parse_args_bye() {
164 assert!(parse_args(args(&["--bye", "Ada"])).unwrap().bye);
165 assert!(!parse_args(args(&["Ada"])).unwrap().bye);
166 }
167
168 #[test]
Add --wave flag that prints o/ after the greeting169 fn parse_args_wave() {
170 assert!(parse_args(args(&["--wave", "Ada"])).unwrap().wave);
171 assert!(!parse_args(args(&["Ada"])).unwrap().wave);
172 }
173
174 #[test]
175 fn wave_is_o_slash() {
176 assert_eq!(wave(), "o/");
177 }
178
179 #[test]
Add --loud flag that ends the greeting with three exclamation marks180 fn parse_args_loud() {
181 assert!(parse_args(args(&["--loud", "Ada"])).unwrap().loud);
Accept -L as a short alias for --loud182 assert!(parse_args(args(&["-L", "Ada"])).unwrap().loud);
Add --loud flag that ends the greeting with three exclamation marks183 assert!(!parse_args(args(&["Ada"])).unwrap().loud);
184 }
185
186 #[test]
187 fn loud_uses_three_exclamation_marks() {
Merge main into wave flag branch188 assert_eq!(loud(&hail("Ada")), "Hi, Ada!!!");
Add --loud flag that ends the greeting with three exclamation marks189 }
190
191 #[test]
Extract argument parsing into parse_args in lib.rs192 fn parse_args_no_args() {
Add --times N flag that prints the greeting N times193 assert_eq!(parse_args(args(&[])).unwrap().name, None);
Extract argument parsing into parse_args in lib.rs194 }
195
196 #[test]
197 fn parse_args_name() {
Add CI workflow for formatting, lints and tests198 assert_eq!(
199 parse_args(args(&["Ada"])).unwrap().name.as_deref(),
200 Some("Ada")
201 );
Extract argument parsing into parse_args in lib.rs202 }
203
204 #[test]
205 fn parse_args_skips_leading_flag() {
206 assert_eq!(
Add CI workflow for formatting, lints and tests207 parse_args(args(&["--unknown", "Ada"]))
208 .unwrap()
209 .name
210 .as_deref(),
Extract argument parsing into parse_args in lib.rs211 Some("Ada")
212 );
213 }
214
Greet people from the command line215 #[test]
Add --shout flag that upper-cases the greeting216 fn parse_args_shout_defaults_off() {
Add --times N flag that prints the greeting N times217 assert!(!parse_args(args(&["Ada"])).unwrap().shout);
Add --shout flag that upper-cases the greeting218 }
219
220 #[test]
221 fn parse_args_shout_before_name() {
Add --times N flag that prints the greeting N times222 let options = parse_args(args(&["--shout", "Ada"])).unwrap();
Add --shout flag that upper-cases the greeting223 assert!(options.shout);
224 assert_eq!(options.name.as_deref(), Some("Ada"));
225 }
226
227 #[test]
228 fn parse_args_shout_after_name() {
Add --times N flag that prints the greeting N times229 let options = parse_args(args(&["Ada", "--shout"])).unwrap();
Add --shout flag that upper-cases the greeting230 assert!(options.shout);
231 assert_eq!(options.name.as_deref(), Some("Ada"));
232 }
233
234 #[test]
235 fn parse_args_shout_alone() {
Add --times N flag that prints the greeting N times236 let options = parse_args(args(&["--shout"])).unwrap();
Add --shout flag that upper-cases the greeting237 assert!(options.shout);
238 assert_eq!(options.name, None);
239 }
240
241 #[test]
Add --version flag that prints the crate version and exits242 fn parse_args_version_defaults_off() {
Add --times N flag that prints the greeting N times243 assert!(!parse_args(args(&["Ada"])).unwrap().version);
Add --version flag that prints the crate version and exits244 }
245
246 #[test]
247 fn parse_args_version() {
Add --times N flag that prints the greeting N times248 let options = parse_args(args(&["--version"])).unwrap();
Add --version flag that prints the crate version and exits249 assert!(options.version);
250 assert_eq!(options.name, None);
251 }
252
253 #[test]
Add --times N flag that prints the greeting N times254 fn parse_args_times_defaults_to_one() {
255 assert_eq!(parse_args(args(&["Ada"])).unwrap().times, 1);
256 }
257
258 #[test]
259 fn parse_args_times_valid() {
260 let options = parse_args(args(&["--times", "3", "Ada"])).unwrap();
261 assert_eq!(options.times, 3);
262 assert_eq!(options.name.as_deref(), Some("Ada"));
263 }
264
265 #[test]
266 fn parse_args_times_missing_value() {
267 assert!(parse_args(args(&["--times"])).is_err());
268 }
269
270 #[test]
271 fn parse_args_times_non_number() {
272 assert!(parse_args(args(&["--times", "abc", "Ada"])).is_err());
273 }
274
275 #[test]
Merge #53: Add a --lang option (en, es, fr) to the greeter276 fn parse_args_lang_defaults_to_en() {
277 assert_eq!(parse_args(args(&["Ada"])).unwrap().lang, Lang::En);
278 }
279
280 #[test]
281 fn parse_args_lang_valid() {
282 let options = parse_args(args(&["--lang", "es", "Ana"])).unwrap();
283 assert_eq!(options.lang, Lang::Es);
284 assert_eq!(options.name.as_deref(), Some("Ana"));
285 assert_eq!(parse_args(args(&["--lang", "fr"])).unwrap().lang, Lang::Fr);
286 }
287
288 #[test]
289 fn parse_args_lang_missing_value() {
290 assert_eq!(
291 parse_args(args(&["--lang"])).unwrap_err(),
292 "--lang requires a value"
293 );
294 }
295
296 #[test]
297 fn parse_args_lang_unknown() {
298 assert_eq!(
299 parse_args(args(&["--lang", "de"])).unwrap_err(),
300 "--lang value must be one of en, es, fr, got \"de\""
301 );
302 }
303
304 #[test]
305 fn greeting_in_each_language() {
306 assert_eq!(greeting_in("Ada", Lang::En), "Hi, Ada!");
307 assert_eq!(greeting_in("Ana", Lang::Es), "¡Hola, Ana!");
308 assert_eq!(greeting_in("Luc", Lang::Fr), "Bonjour, Luc !");
309 }
310
311 #[test]
312 fn greeting_in_trims_and_falls_back_to_world() {
313 assert_eq!(greeting_in(" Ana ", Lang::Es), "¡Hola, Ana!");
314 assert_eq!(greeting_in(" ", Lang::Fr), "Bonjour, world !");
315 }
316
317 #[test]
Add --shout flag that upper-cases the greeting318 fn shout_upper_cases_greeting() {
Merge main into wave flag branch319 assert_eq!(shout(&hail("Ada")), "HI, ADA!");
Add --shout flag that upper-cases the greeting320 }
321
322 #[test]
Greet people from the command line323 fn greets_by_name() {
Merge main into wave flag branch324 assert_eq!(hail("Ada"), "Hi, Ada!");
Greet people from the command line325 }
Handle empty and whitespace-only names, trim whitespace from input326
327 #[test]
328 fn greets_world_with_empty_string() {
Merge main into wave flag branch329 assert_eq!(hail(""), "Hi, world!");
Handle empty and whitespace-only names, trim whitespace from input330 }
331
332 #[test]
333 fn greets_world_with_whitespace_only() {
Merge main into wave flag branch334 assert_eq!(hail(" "), "Hi, world!");
Handle empty and whitespace-only names, trim whitespace from input335 }
336
337 #[test]
338 fn trims_whitespace_from_name() {
Merge main into wave flag branch339 assert_eq!(hail(" Ada "), "Hi, Ada!");
Handle empty and whitespace-only names, trim whitespace from input340 }
Add farewell function alongside greeting341
342 #[test]
343 fn says_goodbye_by_name() {
Merge main into wave flag branch344 assert_eq!(part("Ada"), "Goodbye, Ada!");
Add farewell function alongside greeting345 }
346
347 #[test]
348 fn says_goodbye_to_world_when_blank() {
Merge main into wave flag branch349 assert_eq!(part(" "), "Goodbye, world!");
Add farewell function alongside greeting350 }
351
352 #[test]
353 fn farewell_trims_whitespace() {
Merge main into wave flag branch354 assert_eq!(part(" Ada "), "Goodbye, Ada!");
Add farewell function alongside greeting355 }
Greet people from the command line356}

This file's history is long; its oldest lines are credited to the oldest commit read.