hello/src/lib.rs

297 lines8,417 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 --times N flag that prints the greeting N times39}
40
41impl Default for Options {
42 fn default() -> Self {
43 Options {
44 name: None,
45 shout: false,
46 version: false,
47 times: 1,
Merge #53: Add a --lang option (en, es, fr) to the greeter48 lang: Lang::En,
Add --quiet flag that prints nothing49 quiet: false,
Add --times N flag that prints the greeting N times50 }
51 }
Extract argument parsing into parse_args in lib.rs52}
53
54/// Parses command-line arguments (excluding the program name).
55///
Add --times N flag that prints the greeting N times56/// `--shout` sets [`Options::shout`], `--version` sets
57/// [`Options::version`], and `--times N` sets [`Options::times`] (the
Merge #53: Add a --lang option (en, es, fr) to the greeter58/// next argument is consumed as the value), and `--lang CODE` sets
59/// [`Options::lang`] (likewise consuming the next argument); other
60/// arguments starting with `--` are treated as flags and skipped. The
61/// name is the first remaining argument.
Add --times N flag that prints the greeting N times62///
Merge #53: Add a --lang option (en, es, fr) to the greeter63/// Returns an error message if `--times` has a missing or non-numeric
64/// value, or if `--lang` has a missing or unsupported value.
Add --times N flag that prints the greeting N times65pub fn parse_args<I: IntoIterator<Item = String>>(args: I) -> Result<Options, String> {
Add --shout flag that upper-cases the greeting66 let mut options = Options::default();
Add --times N flag that prints the greeting N times67 let mut args = args.into_iter();
68 while let Some(arg) = args.next() {
Add --shout flag that upper-cases the greeting69 if arg == "--shout" {
70 options.shout = true;
Add --version flag that prints the crate version and exits71 } else if arg == "--version" {
72 options.version = true;
Add --quiet flag that prints nothing73 } else if arg == "--quiet" {
74 options.quiet = true;
Add --times N flag that prints the greeting N times75 } else if arg == "--times" {
76 let value = args
77 .next()
78 .ok_or_else(|| "--times requires a value".to_owned())?;
79 options.times = value
80 .parse()
81 .map_err(|_| format!("--times value must be a number, got {value:?}"))?;
Merge #53: Add a --lang option (en, es, fr) to the greeter82 } else if arg == "--lang" {
83 let value = args
84 .next()
85 .ok_or_else(|| "--lang requires a value".to_owned())?;
86 options.lang = Lang::parse(&value).ok_or_else(|| {
87 format!("--lang value must be one of en, es, fr, got {value:?}")
88 })?;
Add --shout flag that upper-cases the greeting89 } else if !arg.starts_with("--") && options.name.is_none() {
90 options.name = Some(arg);
91 }
92 }
Add --times N flag that prints the greeting N times93 Ok(options)
Extract argument parsing into parse_args in lib.rs94}
95
Add --shout flag that upper-cases the greeting96/// `text` upper-cased.
97pub fn shout(text: &str) -> String {
98 text.to_uppercase()
99}
100
Greet people from the command line101/// The greeting for `name`.
102pub fn greeting(name: &str) -> String {
Merge #53: Add a --lang option (en, es, fr) to the greeter103 greeting_in(name, Lang::En)
104}
105
106/// The greeting for `name` in `lang`.
107pub fn greeting_in(name: &str, lang: Lang) -> String {
Handle empty and whitespace-only names, trim whitespace from input108 let trimmed = name.trim();
109 let display_name = if trimmed.is_empty() { "world" } else { trimmed };
Merge #53: Add a --lang option (en, es, fr) to the greeter110 match lang {
111 Lang::En => format!("Hi, {display_name}!"),
112 Lang::Es => format!("¡Hola, {display_name}!"),
113 Lang::Fr => format!("Bonjour, {display_name} !"),
114 }
Greet people from the command line115}
116
Add farewell function alongside greeting117/// The farewell for `name`.
118pub fn farewell(name: &str) -> String {
119 let trimmed = name.trim();
120 let display_name = if trimmed.is_empty() { "world" } else { trimmed };
121 format!("Goodbye, {display_name}!")
122}
123
Greet people from the command line124#[cfg(test)]
125mod tests {
126 use super::*;
127
Extract argument parsing into parse_args in lib.rs128 fn args(list: &[&str]) -> Vec<String> {
129 list.iter().map(|s| s.to_string()).collect()
130 }
131
132 #[test]
Add --quiet flag that prints nothing133 fn parse_args_quiet() {
134 assert!(parse_args(args(&["--quiet", "Ada"])).unwrap().quiet);
135 assert!(!parse_args(args(&["Ada"])).unwrap().quiet);
136 }
137
138 #[test]
Extract argument parsing into parse_args in lib.rs139 fn parse_args_no_args() {
Add --times N flag that prints the greeting N times140 assert_eq!(parse_args(args(&[])).unwrap().name, None);
Extract argument parsing into parse_args in lib.rs141 }
142
143 #[test]
144 fn parse_args_name() {
Add --times N flag that prints the greeting N times145 assert_eq!(parse_args(args(&["Ada"])).unwrap().name.as_deref(), Some("Ada"));
Extract argument parsing into parse_args in lib.rs146 }
147
148 #[test]
149 fn parse_args_skips_leading_flag() {
150 assert_eq!(
Add --times N flag that prints the greeting N times151 parse_args(args(&["--unknown", "Ada"])).unwrap().name.as_deref(),
Extract argument parsing into parse_args in lib.rs152 Some("Ada")
153 );
154 }
155
Greet people from the command line156 #[test]
Add --shout flag that upper-cases the greeting157 fn parse_args_shout_defaults_off() {
Add --times N flag that prints the greeting N times158 assert!(!parse_args(args(&["Ada"])).unwrap().shout);
Add --shout flag that upper-cases the greeting159 }
160
161 #[test]
162 fn parse_args_shout_before_name() {
Add --times N flag that prints the greeting N times163 let options = parse_args(args(&["--shout", "Ada"])).unwrap();
Add --shout flag that upper-cases the greeting164 assert!(options.shout);
165 assert_eq!(options.name.as_deref(), Some("Ada"));
166 }
167
168 #[test]
169 fn parse_args_shout_after_name() {
Add --times N flag that prints the greeting N times170 let options = parse_args(args(&["Ada", "--shout"])).unwrap();
Add --shout flag that upper-cases the greeting171 assert!(options.shout);
172 assert_eq!(options.name.as_deref(), Some("Ada"));
173 }
174
175 #[test]
176 fn parse_args_shout_alone() {
Add --times N flag that prints the greeting N times177 let options = parse_args(args(&["--shout"])).unwrap();
Add --shout flag that upper-cases the greeting178 assert!(options.shout);
179 assert_eq!(options.name, None);
180 }
181
182 #[test]
Add --version flag that prints the crate version and exits183 fn parse_args_version_defaults_off() {
Add --times N flag that prints the greeting N times184 assert!(!parse_args(args(&["Ada"])).unwrap().version);
Add --version flag that prints the crate version and exits185 }
186
187 #[test]
188 fn parse_args_version() {
Add --times N flag that prints the greeting N times189 let options = parse_args(args(&["--version"])).unwrap();
Add --version flag that prints the crate version and exits190 assert!(options.version);
191 assert_eq!(options.name, None);
192 }
193
194 #[test]
Add --times N flag that prints the greeting N times195 fn parse_args_times_defaults_to_one() {
196 assert_eq!(parse_args(args(&["Ada"])).unwrap().times, 1);
197 }
198
199 #[test]
200 fn parse_args_times_valid() {
201 let options = parse_args(args(&["--times", "3", "Ada"])).unwrap();
202 assert_eq!(options.times, 3);
203 assert_eq!(options.name.as_deref(), Some("Ada"));
204 }
205
206 #[test]
207 fn parse_args_times_missing_value() {
208 assert!(parse_args(args(&["--times"])).is_err());
209 }
210
211 #[test]
212 fn parse_args_times_non_number() {
213 assert!(parse_args(args(&["--times", "abc", "Ada"])).is_err());
214 }
215
216 #[test]
Merge #53: Add a --lang option (en, es, fr) to the greeter217 fn parse_args_lang_defaults_to_en() {
218 assert_eq!(parse_args(args(&["Ada"])).unwrap().lang, Lang::En);
219 }
220
221 #[test]
222 fn parse_args_lang_valid() {
223 let options = parse_args(args(&["--lang", "es", "Ana"])).unwrap();
224 assert_eq!(options.lang, Lang::Es);
225 assert_eq!(options.name.as_deref(), Some("Ana"));
226 assert_eq!(parse_args(args(&["--lang", "fr"])).unwrap().lang, Lang::Fr);
227 }
228
229 #[test]
230 fn parse_args_lang_missing_value() {
231 assert_eq!(
232 parse_args(args(&["--lang"])).unwrap_err(),
233 "--lang requires a value"
234 );
235 }
236
237 #[test]
238 fn parse_args_lang_unknown() {
239 assert_eq!(
240 parse_args(args(&["--lang", "de"])).unwrap_err(),
241 "--lang value must be one of en, es, fr, got \"de\""
242 );
243 }
244
245 #[test]
246 fn greeting_in_each_language() {
247 assert_eq!(greeting_in("Ada", Lang::En), "Hi, Ada!");
248 assert_eq!(greeting_in("Ana", Lang::Es), "¡Hola, Ana!");
249 assert_eq!(greeting_in("Luc", Lang::Fr), "Bonjour, Luc !");
250 }
251
252 #[test]
253 fn greeting_in_trims_and_falls_back_to_world() {
254 assert_eq!(greeting_in(" Ana ", Lang::Es), "¡Hola, Ana!");
255 assert_eq!(greeting_in(" ", Lang::Fr), "Bonjour, world !");
256 }
257
258 #[test]
Add --shout flag that upper-cases the greeting259 fn shout_upper_cases_greeting() {
Merge #42: Greet with Hi instead of Hello260 assert_eq!(shout(&greeting("Ada")), "HI, ADA!");
Add --shout flag that upper-cases the greeting261 }
262
263 #[test]
Greet people from the command line264 fn greets_by_name() {
Merge #42: Greet with Hi instead of Hello265 assert_eq!(greeting("Ada"), "Hi, Ada!");
Greet people from the command line266 }
Handle empty and whitespace-only names, trim whitespace from input267
268 #[test]
269 fn greets_world_with_empty_string() {
Merge #42: Greet with Hi instead of Hello270 assert_eq!(greeting(""), "Hi, world!");
Handle empty and whitespace-only names, trim whitespace from input271 }
272
273 #[test]
274 fn greets_world_with_whitespace_only() {
Merge #42: Greet with Hi instead of Hello275 assert_eq!(greeting(" "), "Hi, world!");
Handle empty and whitespace-only names, trim whitespace from input276 }
277
278 #[test]
279 fn trims_whitespace_from_name() {
Merge #42: Greet with Hi instead of Hello280 assert_eq!(greeting(" Ada "), "Hi, Ada!");
Handle empty and whitespace-only names, trim whitespace from input281 }
Add farewell function alongside greeting282
283 #[test]
284 fn says_goodbye_by_name() {
285 assert_eq!(farewell("Ada"), "Goodbye, Ada!");
286 }
287
288 #[test]
289 fn says_goodbye_to_world_when_blank() {
290 assert_eq!(farewell(" "), "Goodbye, world!");
291 }
292
293 #[test]
294 fn farewell_trims_whitespace() {
295 assert_eq!(farewell(" Ada "), "Goodbye, Ada!");
296 }
Greet people from the command line297}

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