flagon-io/hello

public

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

hello/src/lib.rs

330 lines9,472 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 --times N flag that prints the greeting N times43}
44
45impl Default for Options {
46 fn default() -> Self {
47 Options {
48 name: None,
49 shout: false,
50 version: false,
51 times: 1,
Merge #53: Add a --lang option (en, es, fr) to the greeter52 lang: Lang::En,
Add --quiet flag that prints nothing53 quiet: false,
Add --loud flag that ends the greeting with three exclamation marks54 loud: false,
Merge #64: Add a --bye flag55 bye: false,
Add --times N flag that prints the greeting N times56 }
57 }
Extract argument parsing into parse_args in lib.rs58}
59
60/// Parses command-line arguments (excluding the program name).
61///
Add --times N flag that prints the greeting N times62/// `--shout` sets [`Options::shout`], `--version` sets
63/// [`Options::version`], and `--times N` sets [`Options::times`] (the
Merge #53: Add a --lang option (en, es, fr) to the greeter64/// next argument is consumed as the value), and `--lang CODE` sets
65/// [`Options::lang`] (likewise consuming the next argument); other
66/// arguments starting with `--` are treated as flags and skipped. The
67/// name is the first remaining argument.
Add --times N flag that prints the greeting N times68///
Merge #53: Add a --lang option (en, es, fr) to the greeter69/// Returns an error message if `--times` has a missing or non-numeric
70/// value, or if `--lang` has a missing or unsupported value.
Add --times N flag that prints the greeting N times71pub fn parse_args<I: IntoIterator<Item = String>>(args: I) -> Result<Options, String> {
Add --shout flag that upper-cases the greeting72 let mut options = Options::default();
Add --times N flag that prints the greeting N times73 let mut args = args.into_iter();
74 while let Some(arg) = args.next() {
Add --shout flag that upper-cases the greeting75 if arg == "--shout" {
76 options.shout = true;
Add --version flag that prints the crate version and exits77 } else if arg == "--version" {
78 options.version = true;
Add --quiet flag that prints nothing79 } else if arg == "--quiet" {
80 options.quiet = true;
Accept -L as a short alias for --loud81 } else if arg == "--loud" || arg == "-L" {
Add --loud flag that ends the greeting with three exclamation marks82 options.loud = true;
Merge #64: Add a --bye flag83 } else if arg == "--bye" {
84 options.bye = true;
Add --times N flag that prints the greeting N times85 } else if arg == "--times" {
86 let value = args
87 .next()
88 .ok_or_else(|| "--times requires a value".to_owned())?;
89 options.times = value
90 .parse()
91 .map_err(|_| format!("--times value must be a number, got {value:?}"))?;
Merge #53: Add a --lang option (en, es, fr) to the greeter92 } else if arg == "--lang" {
93 let value = args
94 .next()
95 .ok_or_else(|| "--lang requires a value".to_owned())?;
96 options.lang = Lang::parse(&value).ok_or_else(|| {
97 format!("--lang value must be one of en, es, fr, got {value:?}")
98 })?;
Add --shout flag that upper-cases the greeting99 } else if !arg.starts_with("--") && options.name.is_none() {
100 options.name = Some(arg);
101 }
102 }
Add --times N flag that prints the greeting N times103 Ok(options)
Extract argument parsing into parse_args in lib.rs104}
105
Add --shout flag that upper-cases the greeting106/// `text` upper-cased.
107pub fn shout(text: &str) -> String {
108 text.to_uppercase()
109}
110
Add --loud flag that ends the greeting with three exclamation marks111/// `text` with its final exclamation mark replaced by three.
112pub fn loud(text: &str) -> String {
113 format!("{}!!!", text.strip_suffix('!').unwrap_or(text))
114}
115
Greet people from the command line116/// The greeting for `name`.
Rename greeting() to greet()117pub fn greet(name: &str) -> String {
Merge #53: Add a --lang option (en, es, fr) to the greeter118 greeting_in(name, Lang::En)
119}
120
121/// The greeting for `name` in `lang`.
122pub fn greeting_in(name: &str, lang: Lang) -> String {
Handle empty and whitespace-only names, trim whitespace from input123 let trimmed = name.trim();
124 let display_name = if trimmed.is_empty() { "world" } else { trimmed };
Merge #53: Add a --lang option (en, es, fr) to the greeter125 match lang {
126 Lang::En => format!("Hi, {display_name}!"),
127 Lang::Es => format!("¡Hola, {display_name}!"),
128 Lang::Fr => format!("Bonjour, {display_name} !"),
129 }
Greet people from the command line130}
131
Add farewell function alongside greeting132/// The farewell for `name`.
133pub fn farewell(name: &str) -> String {
134 let trimmed = name.trim();
135 let display_name = if trimmed.is_empty() { "world" } else { trimmed };
136 format!("Goodbye, {display_name}!")
137}
138
Greet people from the command line139#[cfg(test)]
140mod tests {
141 use super::*;
142
Extract argument parsing into parse_args in lib.rs143 fn args(list: &[&str]) -> Vec<String> {
144 list.iter().map(|s| s.to_string()).collect()
145 }
146
147 #[test]
Add --quiet flag that prints nothing148 fn parse_args_quiet() {
149 assert!(parse_args(args(&["--quiet", "Ada"])).unwrap().quiet);
150 assert!(!parse_args(args(&["Ada"])).unwrap().quiet);
151 }
152
153 #[test]
Merge #64: Add a --bye flag154 fn parse_args_bye() {
155 assert!(parse_args(args(&["--bye", "Ada"])).unwrap().bye);
156 assert!(!parse_args(args(&["Ada"])).unwrap().bye);
157 }
158
159 #[test]
Add --loud flag that ends the greeting with three exclamation marks160 fn parse_args_loud() {
161 assert!(parse_args(args(&["--loud", "Ada"])).unwrap().loud);
Accept -L as a short alias for --loud162 assert!(parse_args(args(&["-L", "Ada"])).unwrap().loud);
Add --loud flag that ends the greeting with three exclamation marks163 assert!(!parse_args(args(&["Ada"])).unwrap().loud);
164 }
165
166 #[test]
167 fn loud_uses_three_exclamation_marks() {
Rename greeting() to greet()168 assert_eq!(loud(&greet("Ada")), "Hi, Ada!!!");
Add --loud flag that ends the greeting with three exclamation marks169 }
170
171 #[test]
Extract argument parsing into parse_args in lib.rs172 fn parse_args_no_args() {
Add --times N flag that prints the greeting N times173 assert_eq!(parse_args(args(&[])).unwrap().name, None);
Extract argument parsing into parse_args in lib.rs174 }
175
176 #[test]
177 fn parse_args_name() {
Add --times N flag that prints the greeting N times178 assert_eq!(parse_args(args(&["Ada"])).unwrap().name.as_deref(), Some("Ada"));
Extract argument parsing into parse_args in lib.rs179 }
180
181 #[test]
182 fn parse_args_skips_leading_flag() {
183 assert_eq!(
Add --times N flag that prints the greeting N times184 parse_args(args(&["--unknown", "Ada"])).unwrap().name.as_deref(),
Extract argument parsing into parse_args in lib.rs185 Some("Ada")
186 );
187 }
188
Greet people from the command line189 #[test]
Add --shout flag that upper-cases the greeting190 fn parse_args_shout_defaults_off() {
Add --times N flag that prints the greeting N times191 assert!(!parse_args(args(&["Ada"])).unwrap().shout);
Add --shout flag that upper-cases the greeting192 }
193
194 #[test]
195 fn parse_args_shout_before_name() {
Add --times N flag that prints the greeting N times196 let options = parse_args(args(&["--shout", "Ada"])).unwrap();
Add --shout flag that upper-cases the greeting197 assert!(options.shout);
198 assert_eq!(options.name.as_deref(), Some("Ada"));
199 }
200
201 #[test]
202 fn parse_args_shout_after_name() {
Add --times N flag that prints the greeting N times203 let options = parse_args(args(&["Ada", "--shout"])).unwrap();
Add --shout flag that upper-cases the greeting204 assert!(options.shout);
205 assert_eq!(options.name.as_deref(), Some("Ada"));
206 }
207
208 #[test]
209 fn parse_args_shout_alone() {
Add --times N flag that prints the greeting N times210 let options = parse_args(args(&["--shout"])).unwrap();
Add --shout flag that upper-cases the greeting211 assert!(options.shout);
212 assert_eq!(options.name, None);
213 }
214
215 #[test]
Add --version flag that prints the crate version and exits216 fn parse_args_version_defaults_off() {
Add --times N flag that prints the greeting N times217 assert!(!parse_args(args(&["Ada"])).unwrap().version);
Add --version flag that prints the crate version and exits218 }
219
220 #[test]
221 fn parse_args_version() {
Add --times N flag that prints the greeting N times222 let options = parse_args(args(&["--version"])).unwrap();
Add --version flag that prints the crate version and exits223 assert!(options.version);
224 assert_eq!(options.name, None);
225 }
226
227 #[test]
Add --times N flag that prints the greeting N times228 fn parse_args_times_defaults_to_one() {
229 assert_eq!(parse_args(args(&["Ada"])).unwrap().times, 1);
230 }
231
232 #[test]
233 fn parse_args_times_valid() {
234 let options = parse_args(args(&["--times", "3", "Ada"])).unwrap();
235 assert_eq!(options.times, 3);
236 assert_eq!(options.name.as_deref(), Some("Ada"));
237 }
238
239 #[test]
240 fn parse_args_times_missing_value() {
241 assert!(parse_args(args(&["--times"])).is_err());
242 }
243
244 #[test]
245 fn parse_args_times_non_number() {
246 assert!(parse_args(args(&["--times", "abc", "Ada"])).is_err());
247 }
248
249 #[test]
Merge #53: Add a --lang option (en, es, fr) to the greeter250 fn parse_args_lang_defaults_to_en() {
251 assert_eq!(parse_args(args(&["Ada"])).unwrap().lang, Lang::En);
252 }
253
254 #[test]
255 fn parse_args_lang_valid() {
256 let options = parse_args(args(&["--lang", "es", "Ana"])).unwrap();
257 assert_eq!(options.lang, Lang::Es);
258 assert_eq!(options.name.as_deref(), Some("Ana"));
259 assert_eq!(parse_args(args(&["--lang", "fr"])).unwrap().lang, Lang::Fr);
260 }
261
262 #[test]
263 fn parse_args_lang_missing_value() {
264 assert_eq!(
265 parse_args(args(&["--lang"])).unwrap_err(),
266 "--lang requires a value"
267 );
268 }
269
270 #[test]
271 fn parse_args_lang_unknown() {
272 assert_eq!(
273 parse_args(args(&["--lang", "de"])).unwrap_err(),
274 "--lang value must be one of en, es, fr, got \"de\""
275 );
276 }
277
278 #[test]
279 fn greeting_in_each_language() {
280 assert_eq!(greeting_in("Ada", Lang::En), "Hi, Ada!");
281 assert_eq!(greeting_in("Ana", Lang::Es), "¡Hola, Ana!");
282 assert_eq!(greeting_in("Luc", Lang::Fr), "Bonjour, Luc !");
283 }
284
285 #[test]
286 fn greeting_in_trims_and_falls_back_to_world() {
287 assert_eq!(greeting_in(" Ana ", Lang::Es), "¡Hola, Ana!");
288 assert_eq!(greeting_in(" ", Lang::Fr), "Bonjour, world !");
289 }
290
291 #[test]
Add --shout flag that upper-cases the greeting292 fn shout_upper_cases_greeting() {
Rename greeting() to greet()293 assert_eq!(shout(&greet("Ada")), "HI, ADA!");
Add --shout flag that upper-cases the greeting294 }
295
296 #[test]
Greet people from the command line297 fn greets_by_name() {
Rename greeting() to greet()298 assert_eq!(greet("Ada"), "Hi, Ada!");
Greet people from the command line299 }
Handle empty and whitespace-only names, trim whitespace from input300
301 #[test]
302 fn greets_world_with_empty_string() {
Rename greeting() to greet()303 assert_eq!(greet(""), "Hi, world!");
Handle empty and whitespace-only names, trim whitespace from input304 }
305
306 #[test]
307 fn greets_world_with_whitespace_only() {
Rename greeting() to greet()308 assert_eq!(greet(" "), "Hi, world!");
Handle empty and whitespace-only names, trim whitespace from input309 }
310
311 #[test]
312 fn trims_whitespace_from_name() {
Rename greeting() to greet()313 assert_eq!(greet(" Ada "), "Hi, Ada!");
Handle empty and whitespace-only names, trim whitespace from input314 }
Add farewell function alongside greeting315
316 #[test]
317 fn says_goodbye_by_name() {
318 assert_eq!(farewell("Ada"), "Goodbye, Ada!");
319 }
320
321 #[test]
322 fn says_goodbye_to_world_when_blank() {
323 assert_eq!(farewell(" "), "Goodbye, world!");
324 }
325
326 #[test]
327 fn farewell_trims_whitespace() {
328 assert_eq!(farewell(" Ada "), "Goodbye, Ada!");
329 }
Greet people from the command line330}

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