hello/src/lib.rs

385 lines11,116 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 --version flag that prints the crate version and exits33 /// Whether to print the crate version and exit (`--version`).
34 pub version: bool,
Add --times N flag that prints the greeting N times35 /// How many times to print the greeting (`--times N`).
36 pub times: usize,
Merge #53: Add a --lang option (en, es, fr) to the greeter37 /// The greeting language (`--lang CODE`).
38 pub lang: Lang,
Add --quiet flag that prints nothing39 /// Whether to print nothing (`--quiet`).
40 pub quiet: bool,
Add --loud flag that ends the greeting with three exclamation marks41 /// Whether to end the greeting with three exclamation marks (`--loud`).
42 pub loud: bool,
Merge #64: Add a --bye flag43 /// Whether to print a goodbye after the greeting (`--bye`).
44 pub bye: bool,
Add --wave flag that prints o/ after the greeting45 /// Whether to print a wave after the greeting (`--wave`).
46 pub wave: bool,
Add --times N flag that prints the greeting N times47}
48
49impl Default for Options {
50 fn default() -> Self {
51 Options {
52 name: None,
53 shout: false,
Add --whisper flag54 whisper: false,
Add --times N flag that prints the greeting N times55 version: false,
56 times: 1,
Merge #53: Add a --lang option (en, es, fr) to the greeter57 lang: Lang::En,
Add --quiet flag that prints nothing58 quiet: false,
Add --loud flag that ends the greeting with three exclamation marks59 loud: false,
Merge #64: Add a --bye flag60 bye: false,
Add --wave flag that prints o/ after the greeting61 wave: false,
Add --times N flag that prints the greeting N times62 }
63 }
Extract argument parsing into parse_args in lib.rs64}
65
66/// Parses command-line arguments (excluding the program name).
67///
Add --times N flag that prints the greeting N times68/// `--shout` sets [`Options::shout`], `--version` sets
69/// [`Options::version`], and `--times N` sets [`Options::times`] (the
Merge #53: Add a --lang option (en, es, fr) to the greeter70/// next argument is consumed as the value), and `--lang CODE` sets
71/// [`Options::lang`] (likewise consuming the next argument); other
72/// arguments starting with `--` are treated as flags and skipped. The
73/// name is the first remaining argument.
Add --times N flag that prints the greeting N times74///
Merge #53: Add a --lang option (en, es, fr) to the greeter75/// Returns an error message if `--times` has a missing or non-numeric
76/// value, or if `--lang` has a missing or unsupported value.
Add --times N flag that prints the greeting N times77pub fn parse_args<I: IntoIterator<Item = String>>(args: I) -> Result<Options, String> {
Add --shout flag that upper-cases the greeting78 let mut options = Options::default();
Add --times N flag that prints the greeting N times79 let mut args = args.into_iter();
80 while let Some(arg) = args.next() {
Add --shout flag that upper-cases the greeting81 if arg == "--shout" {
82 options.shout = true;
Add --whisper flag83 options.whisper = false;
84 } else if arg == "--whisper" {
85 options.whisper = true;
86 options.shout = false;
Add --version flag that prints the crate version and exits87 } else if arg == "--version" {
88 options.version = true;
Add --quiet flag that prints nothing89 } else if arg == "--quiet" {
90 options.quiet = true;
Accept -L as a short alias for --loud91 } else if arg == "--loud" || arg == "-L" {
Add --loud flag that ends the greeting with three exclamation marks92 options.loud = true;
Merge #64: Add a --bye flag93 } else if arg == "--bye" {
94 options.bye = true;
Add --wave flag that prints o/ after the greeting95 } else if arg == "--wave" {
96 options.wave = true;
Add --times N flag that prints the greeting N times97 } else if arg == "--times" {
98 let value = args
99 .next()
100 .ok_or_else(|| "--times requires a value".to_owned())?;
101 options.times = value
102 .parse()
103 .map_err(|_| format!("--times value must be a number, got {value:?}"))?;
Merge #53: Add a --lang option (en, es, fr) to the greeter104 } else if arg == "--lang" {
105 let value = args
106 .next()
107 .ok_or_else(|| "--lang requires a value".to_owned())?;
Add CI workflow for formatting, lints and tests108 options.lang = Lang::parse(&value)
109 .ok_or_else(|| format!("--lang value must be one of en, es, fr, got {value:?}"))?;
Add --shout flag that upper-cases the greeting110 } else if !arg.starts_with("--") && options.name.is_none() {
111 options.name = Some(arg);
112 }
113 }
Add --times N flag that prints the greeting N times114 Ok(options)
Extract argument parsing into parse_args in lib.rs115}
116
Add --shout flag that upper-cases the greeting117/// `text` upper-cased.
118pub fn shout(text: &str) -> String {
119 text.to_uppercase()
120}
121
Add --whisper flag122/// `text` lower-cased, with its final exclamation mark replaced by `...`.
123pub fn whisper(text: &str) -> String {
124 let body = text.to_lowercase();
125 format!("{}...", body.trim_end_matches('!').trim_end())
126}
127
Add --loud flag that ends the greeting with three exclamation marks128/// `text` with its final exclamation mark replaced by three.
129pub fn loud(text: &str) -> String {
130 format!("{}!!!", text.strip_suffix('!').unwrap_or(text))
131}
132
Greet people from the command line133/// The greeting for `name`.
Merge main into wave flag branch134pub fn hail(name: &str) -> String {
Merge #53: Add a --lang option (en, es, fr) to the greeter135 greeting_in(name, Lang::En)
136}
137
138/// The greeting for `name` in `lang`.
139pub fn greeting_in(name: &str, lang: Lang) -> String {
Handle empty and whitespace-only names, trim whitespace from input140 let trimmed = name.trim();
141 let display_name = if trimmed.is_empty() { "world" } else { trimmed };
Merge #53: Add a --lang option (en, es, fr) to the greeter142 match lang {
143 Lang::En => format!("Hi, {display_name}!"),
144 Lang::Es => format!("¡Hola, {display_name}!"),
145 Lang::Fr => format!("Bonjour, {display_name} !"),
146 }
Greet people from the command line147}
148
Add --wave flag that prints o/ after the greeting149/// The wave printed by `--wave`.
150pub fn wave() -> &'static str {
151 "o/"
152}
153
Add farewell function alongside greeting154/// The farewell for `name`.
Merge main into wave flag branch155pub fn part(name: &str) -> String {
Add farewell function alongside greeting156 let trimmed = name.trim();
157 let display_name = if trimmed.is_empty() { "world" } else { trimmed };
158 format!("Goodbye, {display_name}!")
159}
160
Greet people from the command line161#[cfg(test)]
162mod tests {
163 use super::*;
164
Extract argument parsing into parse_args in lib.rs165 fn args(list: &[&str]) -> Vec<String> {
166 list.iter().map(|s| s.to_string()).collect()
167 }
168
169 #[test]
Add --quiet flag that prints nothing170 fn parse_args_quiet() {
171 assert!(parse_args(args(&["--quiet", "Ada"])).unwrap().quiet);
172 assert!(!parse_args(args(&["Ada"])).unwrap().quiet);
173 }
174
175 #[test]
Merge #64: Add a --bye flag176 fn parse_args_bye() {
177 assert!(parse_args(args(&["--bye", "Ada"])).unwrap().bye);
178 assert!(!parse_args(args(&["Ada"])).unwrap().bye);
179 }
180
181 #[test]
Add --wave flag that prints o/ after the greeting182 fn parse_args_wave() {
183 assert!(parse_args(args(&["--wave", "Ada"])).unwrap().wave);
184 assert!(!parse_args(args(&["Ada"])).unwrap().wave);
185 }
186
187 #[test]
188 fn wave_is_o_slash() {
189 assert_eq!(wave(), "o/");
190 }
191
192 #[test]
Add --loud flag that ends the greeting with three exclamation marks193 fn parse_args_loud() {
194 assert!(parse_args(args(&["--loud", "Ada"])).unwrap().loud);
Accept -L as a short alias for --loud195 assert!(parse_args(args(&["-L", "Ada"])).unwrap().loud);
Add --loud flag that ends the greeting with three exclamation marks196 assert!(!parse_args(args(&["Ada"])).unwrap().loud);
197 }
198
199 #[test]
200 fn loud_uses_three_exclamation_marks() {
Merge main into wave flag branch201 assert_eq!(loud(&hail("Ada")), "Hi, Ada!!!");
Add --loud flag that ends the greeting with three exclamation marks202 }
203
204 #[test]
Extract argument parsing into parse_args in lib.rs205 fn parse_args_no_args() {
Add --times N flag that prints the greeting N times206 assert_eq!(parse_args(args(&[])).unwrap().name, None);
Extract argument parsing into parse_args in lib.rs207 }
208
209 #[test]
210 fn parse_args_name() {
Add CI workflow for formatting, lints and tests211 assert_eq!(
212 parse_args(args(&["Ada"])).unwrap().name.as_deref(),
213 Some("Ada")
214 );
Extract argument parsing into parse_args in lib.rs215 }
216
217 #[test]
218 fn parse_args_skips_leading_flag() {
219 assert_eq!(
Add CI workflow for formatting, lints and tests220 parse_args(args(&["--unknown", "Ada"]))
221 .unwrap()
222 .name
223 .as_deref(),
Extract argument parsing into parse_args in lib.rs224 Some("Ada")
225 );
226 }
227
Greet people from the command line228 #[test]
Add --shout flag that upper-cases the greeting229 fn parse_args_shout_defaults_off() {
Add --times N flag that prints the greeting N times230 assert!(!parse_args(args(&["Ada"])).unwrap().shout);
Add --shout flag that upper-cases the greeting231 }
232
233 #[test]
234 fn parse_args_shout_before_name() {
Add --times N flag that prints the greeting N times235 let options = parse_args(args(&["--shout", "Ada"])).unwrap();
Add --shout flag that upper-cases the greeting236 assert!(options.shout);
237 assert_eq!(options.name.as_deref(), Some("Ada"));
238 }
239
240 #[test]
241 fn parse_args_shout_after_name() {
Add --times N flag that prints the greeting N times242 let options = parse_args(args(&["Ada", "--shout"])).unwrap();
Add --shout flag that upper-cases the greeting243 assert!(options.shout);
244 assert_eq!(options.name.as_deref(), Some("Ada"));
245 }
246
247 #[test]
248 fn parse_args_shout_alone() {
Add --times N flag that prints the greeting N times249 let options = parse_args(args(&["--shout"])).unwrap();
Add --shout flag that upper-cases the greeting250 assert!(options.shout);
251 assert_eq!(options.name, None);
252 }
253
254 #[test]
Add --version flag that prints the crate version and exits255 fn parse_args_version_defaults_off() {
Add --times N flag that prints the greeting N times256 assert!(!parse_args(args(&["Ada"])).unwrap().version);
Add --version flag that prints the crate version and exits257 }
258
259 #[test]
260 fn parse_args_version() {
Add --times N flag that prints the greeting N times261 let options = parse_args(args(&["--version"])).unwrap();
Add --version flag that prints the crate version and exits262 assert!(options.version);
263 assert_eq!(options.name, None);
264 }
265
266 #[test]
Add --times N flag that prints the greeting N times267 fn parse_args_times_defaults_to_one() {
268 assert_eq!(parse_args(args(&["Ada"])).unwrap().times, 1);
269 }
270
271 #[test]
272 fn parse_args_times_valid() {
273 let options = parse_args(args(&["--times", "3", "Ada"])).unwrap();
274 assert_eq!(options.times, 3);
275 assert_eq!(options.name.as_deref(), Some("Ada"));
276 }
277
278 #[test]
279 fn parse_args_times_missing_value() {
280 assert!(parse_args(args(&["--times"])).is_err());
281 }
282
283 #[test]
284 fn parse_args_times_non_number() {
285 assert!(parse_args(args(&["--times", "abc", "Ada"])).is_err());
286 }
287
288 #[test]
Merge #53: Add a --lang option (en, es, fr) to the greeter289 fn parse_args_lang_defaults_to_en() {
290 assert_eq!(parse_args(args(&["Ada"])).unwrap().lang, Lang::En);
291 }
292
293 #[test]
294 fn parse_args_lang_valid() {
295 let options = parse_args(args(&["--lang", "es", "Ana"])).unwrap();
296 assert_eq!(options.lang, Lang::Es);
297 assert_eq!(options.name.as_deref(), Some("Ana"));
298 assert_eq!(parse_args(args(&["--lang", "fr"])).unwrap().lang, Lang::Fr);
299 }
300
301 #[test]
302 fn parse_args_lang_missing_value() {
303 assert_eq!(
304 parse_args(args(&["--lang"])).unwrap_err(),
305 "--lang requires a value"
306 );
307 }
308
309 #[test]
310 fn parse_args_lang_unknown() {
311 assert_eq!(
312 parse_args(args(&["--lang", "de"])).unwrap_err(),
313 "--lang value must be one of en, es, fr, got \"de\""
314 );
315 }
316
317 #[test]
318 fn greeting_in_each_language() {
319 assert_eq!(greeting_in("Ada", Lang::En), "Hi, Ada!");
320 assert_eq!(greeting_in("Ana", Lang::Es), "¡Hola, Ana!");
321 assert_eq!(greeting_in("Luc", Lang::Fr), "Bonjour, Luc !");
322 }
323
324 #[test]
325 fn greeting_in_trims_and_falls_back_to_world() {
326 assert_eq!(greeting_in(" Ana ", Lang::Es), "¡Hola, Ana!");
327 assert_eq!(greeting_in(" ", Lang::Fr), "Bonjour, world !");
328 }
329
330 #[test]
Add --shout flag that upper-cases the greeting331 fn shout_upper_cases_greeting() {
Merge main into wave flag branch332 assert_eq!(shout(&hail("Ada")), "HI, ADA!");
Add --shout flag that upper-cases the greeting333 }
334
335 #[test]
Add --whisper flag336 fn whisper_lower_cases_and_trails_off() {
337 assert_eq!(whisper(&hail("Ada")), "hi, ada...");
338 assert_eq!(whisper(&greeting_in("Luc", Lang::Fr)), "bonjour, luc...");
339 }
340
341 #[test]
342 fn parse_args_whisper_and_shout_last_wins() {
343 let o = parse_args(args(&["--whisper", "Ada"])).unwrap();
344 assert!(o.whisper && !o.shout);
345 let o = parse_args(args(&["--shout", "--whisper"])).unwrap();
346 assert!(o.whisper && !o.shout);
347 let o = parse_args(args(&["--whisper", "--shout"])).unwrap();
348 assert!(o.shout && !o.whisper);
349 }
350
351 #[test]
Greet people from the command line352 fn greets_by_name() {
Merge main into wave flag branch353 assert_eq!(hail("Ada"), "Hi, Ada!");
Greet people from the command line354 }
Handle empty and whitespace-only names, trim whitespace from input355
356 #[test]
357 fn greets_world_with_empty_string() {
Merge main into wave flag branch358 assert_eq!(hail(""), "Hi, world!");
Handle empty and whitespace-only names, trim whitespace from input359 }
360
361 #[test]
362 fn greets_world_with_whitespace_only() {
Merge main into wave flag branch363 assert_eq!(hail(" "), "Hi, world!");
Handle empty and whitespace-only names, trim whitespace from input364 }
365
366 #[test]
367 fn trims_whitespace_from_name() {
Merge main into wave flag branch368 assert_eq!(hail(" Ada "), "Hi, Ada!");
Handle empty and whitespace-only names, trim whitespace from input369 }
Add farewell function alongside greeting370
371 #[test]
372 fn says_goodbye_by_name() {
Merge main into wave flag branch373 assert_eq!(part("Ada"), "Goodbye, Ada!");
Add farewell function alongside greeting374 }
375
376 #[test]
377 fn says_goodbye_to_world_when_blank() {
Merge main into wave flag branch378 assert_eq!(part(" "), "Goodbye, world!");
Add farewell function alongside greeting379 }
380
381 #[test]
382 fn farewell_trims_whitespace() {
Merge main into wave flag branch383 assert_eq!(part(" Ada "), "Goodbye, Ada!");
Add farewell function alongside greeting384 }
Greet people from the command line385}

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