Skip to content

Commit 3b8b524

Browse files
committed
add expected output for chapter 3
1 parent 80c1e90 commit 3b8b524

File tree

10 files changed

+414
-105
lines changed

10 files changed

+414
-105
lines changed

cli-book/ch03_catr/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ edition = "2021"
55

66
[dependencies]
77
clap = "4.5.23"
8+
anyhow = "1.0.79"
89

910
[dev-dependencies]
1011
assert_cmd = "2.0.13"
1112
predicates = "3.0.4"
12-
rand = "0.8"
13+
rand = "0.8"
14+
pretty_assertions = "1.4.0"

cli-book/ch03_catr/src/lib.rs

Lines changed: 145 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,94 +1,145 @@
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-
}
1+
use clap::{Arg, ArgAction, Command};
2+
use std::error::Error;
3+
use std::fs::File;
4+
use std::io::{self, 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+
// method 1
24+
// pub fn run(config: Config) -> MyResult<()> {
25+
// // dbg!(config);
26+
// for filename in config.files {
27+
// // println!("{}", filename);
28+
// match open(&filename) {
29+
// Err(err) => eprintln!("Failed to open {}: {}", filename, err),
30+
// // Ok(_) => println!("Opened {}", filename),
31+
// Ok(file) => {
32+
// let mut line_number = 0;
33+
// for line_result in file.lines() {
34+
// let line = line_result?;
35+
// line_number += 1;
36+
37+
// if config.number_lines {
38+
// println!("{:>6}\t{}", line_number, line);
39+
// } else {
40+
// println!("{}", line);
41+
// }
42+
43+
// if config.number_nonblank_lines {
44+
// if line.is_empty() {
45+
// line_number -= 1;
46+
// continue;
47+
// } else {
48+
// println!("{:>6}\t{}", line_number, line);
49+
// }
50+
// }
51+
// }
52+
// }
53+
// }
54+
// }
55+
// Ok(())
56+
// }
57+
58+
// method 2 - Idomatic way
59+
pub fn run(config: Config) -> MyResult<()> {
60+
for filename in config.files {
61+
match open(&filename) {
62+
Err(err) => eprintln!("Failed to open {}: {}", filename, err),
63+
Ok(file) => {
64+
let mut last_num = 0;
65+
for (line_num, line) in file.lines().enumerate() {
66+
let line = line?; // unwrapping from Result
67+
if config.number_lines {
68+
println!("{:6}\t{}", line_num + 1, line);
69+
} else if config.number_nonblank_lines {
70+
if !line.is_empty() {
71+
last_num += 1;
72+
println!("{:6}\t{}", last_num, line);
73+
} else {
74+
println!();
75+
}
76+
} else {
77+
println!("{}", line);
78+
}
79+
}
80+
}
81+
}
82+
}
83+
Ok(())
84+
}
85+
86+
pub fn get_args() -> MyResult<Config> {
87+
let matches = Command::new("ch03_catr")
88+
.version("0.1.0")
89+
.author("Rakuram <[email protected]>")
90+
.about("Rust cat Command")
91+
.help_template(
92+
"{name} {version} \n\
93+
{author} \n\
94+
{about} \n\n\
95+
USAGE: \n {usage}\n\n\
96+
{all-args}",
97+
)
98+
.arg(
99+
Arg::new("files")
100+
.value_name("FILE")
101+
.help("Input files")
102+
// .required(true) // reason why required is not used is because we are using default value
103+
.num_args(1..)
104+
.default_value("-")
105+
.action(ArgAction::Append),
106+
)
107+
.arg(
108+
Arg::new("number_lines")
109+
.short('n')
110+
.long("number")
111+
.help("number all output lines")
112+
.action(ArgAction::SetTrue)
113+
.conflicts_with("number_nonblank"),
114+
)
115+
.arg(
116+
Arg::new("number_nonblank")
117+
.short('b')
118+
.long("number-nonblank")
119+
.help("number nonempty output lines, overrides -n")
120+
.action(ArgAction::SetTrue),
121+
)
122+
.get_matches();
123+
124+
// let text: Vec<String> = matches
125+
// .get_many("files")
126+
// .expect("filename is required")
127+
// .cloned()
128+
// .collect();
129+
130+
// println!("{:#?}", text);
131+
132+
// let number_lines = matches.get_flag("number_lines");
133+
134+
// let number_nonblank_lines = matches.get_flag("number_nonblank_lines");
135+
136+
// println!("text: {:#?}", text);
137+
// println!("number_lines: {:#?}", number_lines);
138+
// println!("number_nonblank_lines: {:#?}", number_nonblank_lines);
139+
140+
Ok(Config {
141+
files: matches.get_many("files").unwrap().cloned().collect(),
142+
number_lines: matches.get_flag("number_lines"),
143+
number_nonblank_lines: matches.get_flag("number_nonblank"),
144+
})
145+
}

0 commit comments

Comments
 (0)