Skip to content

Commit 80c1e90

Browse files
committed
add chapter 3
1 parent 82c5699 commit 80c1e90

25 files changed

+176
-0
lines changed

cli-book/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ resolver = "2"
44
members = [
55
"ch01_cli_rs",
66
"ch02_echor",
7+
"ch03_catr",
78
]

cli-book/ch03_catr/Cargo.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "ch03_catr"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
clap = "4.5.23"
8+
9+
[dev-dependencies]
10+
assert_cmd = "2.0.13"
11+
predicates = "3.0.4"
12+
rand = "0.8"

cli-book/ch03_catr/mk-outs.sh

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#!/usr/bin/env bash
2+
3+
set -u
4+
5+
ROOT="tests/inputs"
6+
OUT_DIR="tests/expected"
7+
8+
[[ ! -d "$OUT_DIR" ]] && mkdir -p "$OUT_DIR"
9+
10+
EMPTY="$ROOT/empty.txt"
11+
FOX="$ROOT/fox.txt"
12+
SPIDERS="$ROOT/spiders.txt"
13+
BUSTLE="$ROOT/the-bustle.txt"
14+
ALL="$EMPTY $FOX $SPIDERS $BUSTLE"
15+
16+
for FILE in $ALL; do
17+
BASENAME=$(basename "$FILE")
18+
cat $FILE > ${OUT_DIR}/${BASENAME}.out
19+
cat -n $FILE > ${OUT_DIR}/${BASENAME}.n.out
20+
cat -b $FILE > ${OUT_DIR}/${BASENAME}.b.out
21+
done
22+
23+
cat $ALL > $OUT_DIR/all.out
24+
cat -n $ALL > $OUT_DIR/all.n.out
25+
cat -b $ALL > $OUT_DIR/all.b.out
26+
27+
cat < $BUSTLE > $OUT_DIR/$(basename $BUSTLE).stdin.out
28+
cat -n < $BUSTLE > $OUT_DIR/$(basename $BUSTLE).n.stdin.out
29+
cat -b < $BUSTLE > $OUT_DIR/$(basename $BUSTLE).b.stdin.out

cli-book/ch03_catr/src/lib.rs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
use std::error::Error;
2+
use clap::{Arg, ArgAction, Command};
3+
use std::fs::File;
4+
use std::io::{self, stdin, BufRead, BufReader};
5+
6+
#[allow(unused)]
7+
#[derive(Debug)]
8+
pub struct Config {
9+
files: Vec<String>,
10+
number_lines: bool,
11+
number_nonblank_lines: bool,
12+
}
13+
14+
type MyResult<T> = Result<T, Box<dyn Error>>;
15+
16+
fn open(filename: &str) -> MyResult<Box<dyn BufRead>> {
17+
match filename {
18+
"-" => Ok(Box::new(BufReader::new(io::stdin()))),
19+
_ => Ok(Box::new(BufReader::new(File::open(filename)?))),
20+
}
21+
}
22+
23+
pub fn run(config: Config) -> MyResult<()> {
24+
// dbg!(config);
25+
for filename in config.files {
26+
// println!("{}", filename);
27+
match open(&filename) {
28+
Err(err) => eprintln!("Failed to open {}: {}", filename, err),
29+
Ok(_) => println!("Opened {}", filename),
30+
}
31+
}
32+
Ok(())
33+
}
34+
35+
pub fn get_args() -> MyResult<Config> {
36+
let matches = Command::new("ch03_catr")
37+
.version("0.1.0")
38+
.author("Rakuram <[email protected]>")
39+
.about("Rust cat Command")
40+
.help_template(
41+
"{name} {version} \n\
42+
{author} \n\
43+
{about} \n\n\
44+
USAGE: \n {usage}\n\n\
45+
{all-args}",
46+
)
47+
.arg(
48+
Arg::new("files")
49+
.value_name("FILE")
50+
.help("Input files")
51+
// .required(true) // reason why required is not used is because we are using default value
52+
.num_args(1..)
53+
.default_value("-")
54+
.action(ArgAction::Append),
55+
)
56+
.arg(
57+
Arg::new("number_lines")
58+
.short('n')
59+
.long("number")
60+
.help("number all output lines")
61+
.action(ArgAction::SetTrue)
62+
.conflicts_with("number_nonblank"),
63+
)
64+
.arg(
65+
Arg::new("number_nonblank")
66+
.short('b')
67+
.long("number-nonblank")
68+
.help("number nonempty output lines, overrides -n")
69+
.action(ArgAction::SetTrue),
70+
)
71+
.get_matches();
72+
73+
// let text: Vec<String> = matches
74+
// .get_many("files")
75+
// .expect("filename is required")
76+
// .cloned()
77+
// .collect();
78+
79+
// println!("{:#?}", text);
80+
81+
// let number_lines = matches.get_flag("number_lines");
82+
83+
// let number_nonblank_lines = matches.get_flag("number_nonblank_lines");
84+
85+
// println!("text: {:#?}", text);
86+
// println!("number_lines: {:#?}", number_lines);
87+
// println!("number_nonblank_lines: {:#?}", number_nonblank_lines);
88+
89+
Ok(Config {
90+
files: matches.get_many("files").unwrap().cloned().collect(),
91+
number_lines: matches.get_flag("number_lines"),
92+
number_nonblank_lines: matches.get_flag("number_nonblank"),
93+
})
94+
}

cli-book/ch03_catr/src/main.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
fn main() {
2+
if let Err(e) = ch03_catr::get_args().and_then(ch03_catr::run) {
3+
eprintln!("{}", e);
4+
std::process::exit(1);
5+
}
6+
}

cli-book/ch03_catr/tests/cli.rs

Whitespace-only changes.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
1 The quick brown fox jumps over the lazy dog.Don't worry, spiders,
2+
2 I keep house
3+
3 casually.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
1 The quick brown fox jumps over the lazy dog.Don't worry, spiders,
2+
2 I keep house
3+
3 casually.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
The quick brown fox jumps over the lazy dog.Don't worry, spiders,
2+
I keep house
3+
casually.

cli-book/ch03_catr/tests/expected/empty.txt.b.out

Whitespace-only changes.

0 commit comments

Comments
 (0)