flagon-io/hello

public

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

hello/src/lib.rs

406 lines11,728 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 --whisper flag31 /// Whether to lower-case the greeting and end it with `...` (`--whisper`).
32 pub whisper: bool,
Add --reverse flag33 /// Whether to spell the name backwards (`--reverse`).
34 pub reverse: bool,
Add --version flag that prints the crate version and exits35 /// Whether to print the crate version and exit (`--version`).
36 pub version: bool,
Add --times N flag that prints the greeting N times37 /// How many times to print the greeting (`--times N`).
38 pub times: usize,
Merge #53: Add a --lang option (en, es, fr) to the greeter39 /// The greeting language (`--lang CODE`).
40 pub lang: Lang,
Add --quiet flag that prints nothing41 /// Whether to print nothing (`--quiet`).
42 pub quiet: bool,
Add --loud flag that ends the greeting with three exclamation marks43 /// Whether to end the greeting with three exclamation marks (`--loud`).
44 pub loud: bool,
Merge #64: Add a --bye flag45 /// Whether to print a goodbye after the greeting (`--bye`).
46 pub bye: bool,
Add --wave flag that prints o/ after the greeting47 /// Whether to print a wave after the greeting (`--wave`).
48 pub wave: bool,
Add --times N flag that prints the greeting N times49}
50
51impl Default for Options {
52 fn default() -> Self {
53 Options {
54 name: None,
55 shout: false,
Add --whisper flag56 whisper: false,
Add --reverse flag57 reverse: false,
Add --times N flag that prints the greeting N times58 version: false,
59 times: 1,
Merge #53: Add a --lang option (en, es, fr) to the greeter60 lang: Lang::En,
Add --quiet flag that prints nothing61 quiet: false,
Add --loud flag that ends the greeting with three exclamation marks62 loud: false,
Merge #64: Add a --bye flag63 bye: false,
Add --wave flag that prints o/ after the greeting64 wave: false,
Add --times N flag that prints the greeting N times65 }
66 }
Extract argument parsing into parse_args in lib.rs67}
68
69/// Parses command-line arguments (excluding the program name).
70///
Add --times N flag that prints the greeting N times71/// `--shout` sets [`Options::shout`], `--version` sets
72/// [`Options::version`], and `--times N` sets [`Options::times`] (the
Merge #53: Add a --lang option (en, es, fr) to the greeter73/// next argument is consumed as the value), and `--lang CODE` sets
74/// [`Options::lang`] (likewise consuming the next argument); other
75/// arguments starting with `--` are treated as flags and skipped. The
76/// name is the first remaining argument.
Add --times N flag that prints the greeting N times77///
Merge #53: Add a --lang option (en, es, fr) to the greeter78/// Returns an error message if `--times` has a missing or non-numeric
79/// value, or if `--lang` has a missing or unsupported value.
Add --times N flag that prints the greeting N times80pub fn parse_args<I: IntoIterator<Item = String>>(args: I) -> Result<Options, String> {
Add --shout flag that upper-cases the greeting81 let mut options = Options::default();
Add --times N flag that prints the greeting N times82 let mut args = args.into_iter();
83 while let Some(arg) = args.next() {
Add --shout flag that upper-cases the greeting84 if arg == "--shout" {
85 options.shout = true;
Add --whisper flag86 options.whisper = false;
87 } else if arg == "--whisper" {
88 options.whisper = true;
89 options.shout = false;
Add --reverse flag90 } else if arg == "--reverse" {
91 options.reverse = true;
Add --version flag that prints the crate version and exits92 } else if arg == "--version" {
93 options.version = true;
Add --quiet flag that prints nothing94 } else if arg == "--quiet" {
95 options.quiet = true;
Accept -L as a short alias for --loud96 } else if arg == "--loud" || arg == "-L" {
Add --loud flag that ends the greeting with three exclamation marks97 options.loud = true;
Merge #64: Add a --bye flag98 } else if arg == "--bye" {
99 options.bye = true;
Add --wave flag that prints o/ after the greeting100 } else if arg == "--wave" {
101 options.wave = true;
Add --times N flag that prints the greeting N times102 } else if arg == "--times" {
103 let value = args
104 .next()
105 .ok_or_else(|| "--times requires a value".to_owned())?;
106 options.times = value
107 .parse()
108 .map_err(|_| format!("--times value must be a number, got {value:?}"))?;
Merge #53: Add a --lang option (en, es, fr) to the greeter109 } else if arg == "--lang" {
110 let value = args
111 .next()
112 .ok_or_else(|| "--lang requires a value".to_owned())?;
Add CI workflow for formatting, lints and tests113 options.lang = Lang::parse(&value)
114 .ok_or_else(|| format!("--lang value must be one of en, es, fr, got {value:?}"))?;
Add --shout flag that upper-cases the greeting115 } else if !arg.starts_with("--") && options.name.is_none() {
116 options.name = Some(arg);
117 }
118 }
Add --times N flag that prints the greeting N times119 Ok(options)
Extract argument parsing into parse_args in lib.rs120}
121
Add --reverse flag122/// `name` spelled backwards.
123pub fn reverse(name: &str) -> String {
124 name.chars().rev().collect()
125}
126
Add --shout flag that upper-cases the greeting127/// `text` upper-cased.
128pub fn shout(text: &str) -> String {
129 text.to_uppercase()
130}
131
Add --whisper flag132/// `text` lower-cased, with its final exclamation mark replaced by `...`.
133pub fn whisper(text: &str) -> String {
134 let body = text.to_lowercase();
135 format!("{}...", body.trim_end_matches('!').trim_end())
136}
137
Add --loud flag that ends the greeting with three exclamation marks138/// `text` with its final exclamation mark replaced by three.
139pub fn loud(text: &str) -> String {
140 format!("{}!!!", text.strip_suffix('!').unwrap_or(text))
141}
142
Greet people from the command line143/// The greeting for `name`.
Rename hail() and part() to greet() and farewell()144pub fn greet(name: &str) -> String {
Merge #53: Add a --lang option (en, es, fr) to the greeter145 greeting_in(name, Lang::En)
146}
147
148/// The greeting for `name` in `lang`.
149pub fn greeting_in(name: &str, lang: Lang) -> String {
Handle empty and whitespace-only names, trim whitespace from input150 let trimmed = name.trim();
151 let display_name = if trimmed.is_empty() { "world" } else { trimmed };
Merge #53: Add a --lang option (en, es, fr) to the greeter152 match lang {
153 Lang::En => format!("Hi, {display_name}!"),
154 Lang::Es => format!("¡Hola, {display_name}!"),
155 Lang::Fr => format!("Bonjour, {display_name} !"),
156 }
Greet people from the command line157}
158
Add --wave flag that prints o/ after the greeting159/// The wave printed by `--wave`.
160pub fn wave() -> &'static str {
161 "o/"
162}
163
Add farewell function alongside greeting164/// The farewell for `name`.
Rename hail() and part() to greet() and farewell()165pub fn farewell(name: &str) -> String {
Add farewell function alongside greeting166 let trimmed = name.trim();
167 let display_name = if trimmed.is_empty() { "world" } else { trimmed };
168 format!("Goodbye, {display_name}!")
169}
170
Greet people from the command line171#[cfg(test)]
172mod tests {
173 use super::*;
174
Extract argument parsing into parse_args in lib.rs175 fn args(list: &[&str]) -> Vec<String> {
176 list.iter().map(|s| s.to_string()).collect()
177 }
178
179 #[test]
Add --quiet flag that prints nothing180 fn parse_args_quiet() {
181 assert!(parse_args(args(&["--quiet", "Ada"])).unwrap().quiet);
182 assert!(!parse_args(args(&["Ada"])).unwrap().quiet);
183 }
184
185 #[test]
Merge #64: Add a --bye flag186 fn parse_args_bye() {
187 assert!(parse_args(args(&["--bye", "Ada"])).unwrap().bye);
188 assert!(!parse_args(args(&["Ada"])).unwrap().bye);
189 }
190
191 #[test]
Add --wave flag that prints o/ after the greeting192 fn parse_args_wave() {
193 assert!(parse_args(args(&["--wave", "Ada"])).unwrap().wave);
194 assert!(!parse_args(args(&["Ada"])).unwrap().wave);
195 }
196
197 #[test]
198 fn wave_is_o_slash() {
199 assert_eq!(wave(), "o/");
200 }
201
202 #[test]
Add --loud flag that ends the greeting with three exclamation marks203 fn parse_args_loud() {
204 assert!(parse_args(args(&["--loud", "Ada"])).unwrap().loud);
Accept -L as a short alias for --loud205 assert!(parse_args(args(&["-L", "Ada"])).unwrap().loud);
Add --loud flag that ends the greeting with three exclamation marks206 assert!(!parse_args(args(&["Ada"])).unwrap().loud);
207 }
208
209 #[test]
210 fn loud_uses_three_exclamation_marks() {
Rename hail() and part() to greet() and farewell()211 assert_eq!(loud(&greet("Ada")), "Hi, Ada!!!");
Add --loud flag that ends the greeting with three exclamation marks212 }
213
214 #[test]
Extract argument parsing into parse_args in lib.rs215 fn parse_args_no_args() {
Add --times N flag that prints the greeting N times216 assert_eq!(parse_args(args(&[])).unwrap().name, None);
Extract argument parsing into parse_args in lib.rs217 }
218
219 #[test]
220 fn parse_args_name() {
Add CI workflow for formatting, lints and tests221 assert_eq!(
222 parse_args(args(&["Ada"])).unwrap().name.as_deref(),
223 Some("Ada")
224 );
Extract argument parsing into parse_args in lib.rs225 }
226
227 #[test]
228 fn parse_args_skips_leading_flag() {
229 assert_eq!(
Add CI workflow for formatting, lints and tests230 parse_args(args(&["--unknown", "Ada"]))
231 .unwrap()
232 .name
233 .as_deref(),
Extract argument parsing into parse_args in lib.rs234 Some("Ada")
235 );
236 }
237
Greet people from the command line238 #[test]
Add --shout flag that upper-cases the greeting239 fn parse_args_shout_defaults_off() {
Add --times N flag that prints the greeting N times240 assert!(!parse_args(args(&["Ada"])).unwrap().shout);
Add --shout flag that upper-cases the greeting241 }
242
243 #[test]
244 fn parse_args_shout_before_name() {
Add --times N flag that prints the greeting N times245 let options = parse_args(args(&["--shout", "Ada"])).unwrap();
Add --shout flag that upper-cases the greeting246 assert!(options.shout);
247 assert_eq!(options.name.as_deref(), Some("Ada"));
248 }
249
250 #[test]
251 fn parse_args_shout_after_name() {
Add --times N flag that prints the greeting N times252 let options = parse_args(args(&["Ada", "--shout"])).unwrap();
Add --shout flag that upper-cases the greeting253 assert!(options.shout);
254 assert_eq!(options.name.as_deref(), Some("Ada"));
255 }
256
257 #[test]
258 fn parse_args_shout_alone() {
Add --times N flag that prints the greeting N times259 let options = parse_args(args(&["--shout"])).unwrap();
Add --shout flag that upper-cases the greeting260 assert!(options.shout);
261 assert_eq!(options.name, None);
262 }
263
264 #[test]
Add --version flag that prints the crate version and exits265 fn parse_args_version_defaults_off() {
Add --times N flag that prints the greeting N times266 assert!(!parse_args(args(&["Ada"])).unwrap().version);
Add --version flag that prints the crate version and exits267 }
268
269 #[test]
270 fn parse_args_version() {
Add --times N flag that prints the greeting N times271 let options = parse_args(args(&["--version"])).unwrap();
Add --version flag that prints the crate version and exits272 assert!(options.version);
273 assert_eq!(options.name, None);
274 }
275
276 #[test]
Add --times N flag that prints the greeting N times277 fn parse_args_times_defaults_to_one() {
278 assert_eq!(parse_args(args(&["Ada"])).unwrap().times, 1);
279 }
280
281 #[test]
282 fn parse_args_times_valid() {
283 let options = parse_args(args(&["--times", "3", "Ada"])).unwrap();
284 assert_eq!(options.times, 3);
285 assert_eq!(options.name.as_deref(), Some("Ada"));
286 }
287
288 #[test]
289 fn parse_args_times_missing_value() {
290 assert!(parse_args(args(&["--times"])).is_err());
291 }
292
293 #[test]
294 fn parse_args_times_non_number() {
295 assert!(parse_args(args(&["--times", "abc", "Ada"])).is_err());
296 }
297
298 #[test]
Merge #53: Add a --lang option (en, es, fr) to the greeter299 fn parse_args_lang_defaults_to_en() {
300 assert_eq!(parse_args(args(&["Ada"])).unwrap().lang, Lang::En);
301 }
302
303 #[test]
304 fn parse_args_lang_valid() {
305 let options = parse_args(args(&["--lang", "es", "Ana"])).unwrap();
306 assert_eq!(options.lang, Lang::Es);
307 assert_eq!(options.name.as_deref(), Some("Ana"));
308 assert_eq!(parse_args(args(&["--lang", "fr"])).unwrap().lang, Lang::Fr);
309 }
310
311 #[test]
312 fn parse_args_lang_missing_value() {
313 assert_eq!(
314 parse_args(args(&["--lang"])).unwrap_err(),
315 "--lang requires a value"
316 );
317 }
318
319 #[test]
320 fn parse_args_lang_unknown() {
321 assert_eq!(
322 parse_args(args(&["--lang", "de"])).unwrap_err(),
323 "--lang value must be one of en, es, fr, got \"de\""
324 );
325 }
326
327 #[test]
328 fn greeting_in_each_language() {
329 assert_eq!(greeting_in("Ada", Lang::En), "Hi, Ada!");
330 assert_eq!(greeting_in("Ana", Lang::Es), "¡Hola, Ana!");
331 assert_eq!(greeting_in("Luc", Lang::Fr), "Bonjour, Luc !");
332 }
333
334 #[test]
335 fn greeting_in_trims_and_falls_back_to_world() {
336 assert_eq!(greeting_in(" Ana ", Lang::Es), "¡Hola, Ana!");
337 assert_eq!(greeting_in(" ", Lang::Fr), "Bonjour, world !");
338 }
339
340 #[test]
Add --shout flag that upper-cases the greeting341 fn shout_upper_cases_greeting() {
Rename hail() and part() to greet() and farewell()342 assert_eq!(shout(&greet("Ada")), "HI, ADA!");
Add --shout flag that upper-cases the greeting343 }
344
345 #[test]
Add --whisper flag346 fn whisper_lower_cases_and_trails_off() {
Rename hail() and part() to greet() and farewell()347 assert_eq!(whisper(&greet("Ada")), "hi, ada...");
Add --whisper flag348 assert_eq!(whisper(&greeting_in("Luc", Lang::Fr)), "bonjour, luc...");
349 }
350
351 #[test]
352 fn parse_args_whisper_and_shout_last_wins() {
353 let o = parse_args(args(&["--whisper", "Ada"])).unwrap();
354 assert!(o.whisper && !o.shout);
355 let o = parse_args(args(&["--shout", "--whisper"])).unwrap();
356 assert!(o.whisper && !o.shout);
357 let o = parse_args(args(&["--whisper", "--shout"])).unwrap();
358 assert!(o.shout && !o.whisper);
359 }
360
361 #[test]
Add --reverse flag362 fn parse_args_reverse() {
363 assert!(parse_args(args(&["--reverse", "Ada"])).unwrap().reverse);
364 assert!(!parse_args(args(&["Ada"])).unwrap().reverse);
365 }
366
367 #[test]
368 fn reverse_spells_backwards() {
Rename hail() and part() to greet() and farewell()369 assert_eq!(greet(&reverse("Ada")), "Hi, adA!");
Add --reverse flag370 }
371
372 #[test]
Greet people from the command line373 fn greets_by_name() {
Rename hail() and part() to greet() and farewell()374 assert_eq!(greet("Ada"), "Hi, Ada!");
Greet people from the command line375 }
Handle empty and whitespace-only names, trim whitespace from input376
377 #[test]
378 fn greets_world_with_empty_string() {
Rename hail() and part() to greet() and farewell()379 assert_eq!(greet(""), "Hi, world!");
Handle empty and whitespace-only names, trim whitespace from input380 }
381
382 #[test]
383 fn greets_world_with_whitespace_only() {
Rename hail() and part() to greet() and farewell()384 assert_eq!(greet(" "), "Hi, world!");
Handle empty and whitespace-only names, trim whitespace from input385 }
386
387 #[test]
388 fn trims_whitespace_from_name() {
Rename hail() and part() to greet() and farewell()389 assert_eq!(greet(" Ada "), "Hi, Ada!");
Handle empty and whitespace-only names, trim whitespace from input390 }
Add farewell function alongside greeting391
392 #[test]
393 fn says_goodbye_by_name() {
Rename hail() and part() to greet() and farewell()394 assert_eq!(farewell("Ada"), "Goodbye, Ada!");
Add farewell function alongside greeting395 }
396
397 #[test]
398 fn says_goodbye_to_world_when_blank() {
Rename hail() and part() to greet() and farewell()399 assert_eq!(farewell(" "), "Goodbye, world!");
Add farewell function alongside greeting400 }
401
402 #[test]
403 fn farewell_trims_whitespace() {
Rename hail() and part() to greet() and farewell()404 assert_eq!(farewell(" Ada "), "Goodbye, Ada!");
Add farewell function alongside greeting405 }
Greet people from the command line406}

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