1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
use std::io::{self, BufRead};
use std::collections::HashSet;
use std::iter::{FromIterator, Iterator};
use std::str::FromStr;
use clap::ArgMatches;
use clap::clap_app;
const LOWEST: usize = 0;
const LINES: &str = "LINES";
const REVERSE: &str = "reverse";
type LineNums = Vec<usize>;
type NthStr = (usize, String);
fn get_matches_from_cli<'a>() -> ArgMatches<'a> {
clap_app!(nth =>
(version: "0.0.1")
(author: "AlexDeLorenzo.dev")
(about:
"Return the contents of stdin from the line numbers supplied as arguments.")
(@arg LINES: +required ... "Line numbers to select")
(@arg reverse: -r --reverse
"Write every line, except the line numbers supplied as LINES, from stdin to stdout.")
)
.get_matches()
}
pub fn run() {
let matches = get_matches_from_cli();
let mut line_nums = get_line_nums(&matches);
let stdin = io::stdin();
let nth_lines = stdin.lock()
.lines()
.map(|line| line.unwrap())
.enumerate();
if matches.is_present(REVERSE) {
exclude_lines(&line_nums, nth_lines);
} else {
include_lines(&mut line_nums, nth_lines);
}
}
fn get_line_nums(matches: &ArgMatches) -> LineNums {
let mut line_nums =
if let Some(lines) = matches.values_of(LINES) {
Vec::from_iter(lines.map(|line|
FromStr::from_str(line).unwrap()
))
} else {
vec![]
};
line_nums.sort();
line_nums
}
fn include_lines<T: IntoIterator<Item = NthStr>>(
lines: &mut LineNums,
content: T
) {
for (nth, line) in content {
if nth == lines[LOWEST] {
println!("{}", line);
lines.remove(LOWEST);
}
if lines.is_empty() {
break
}
}
}
fn exclude_lines<T: Iterator<Item = NthStr>>(
lines: &LineNums,
content: T
) {
let mut lines: HashSet<usize> =
HashSet::from_iter(lines.iter().cloned());
for (nth, line) in content {
if lines.contains(&nth) {
lines.remove(&nth);
} else {
println!("{}", line);
}
}
}