1use clap::Parser;
2use rustutils_runnable::Runnable;
3use std::collections::BTreeMap;
4use std::error::Error;
5use std::fs::File;
6use std::io::{self, stdin, BufReader, Read};
7use std::path::PathBuf;
8
9#[derive(Parser, Clone, Debug)]
11#[clap(author, version, about, long_about = None)]
12pub struct Wc {
13 #[clap(long, short)]
15 pub bytes: bool,
16 #[clap(long, short)]
18 pub chars: bool,
19 #[clap(long, short)]
21 pub lines: bool,
22 #[clap(long, short)]
24 pub words: bool,
25 #[clap(long, short)]
27 pub json: bool,
28 pub files: Vec<PathBuf>,
30}
31
32impl Wc {
33 pub fn count_file(&self, file: &mut dyn Read) -> Result<WordCount, io::Error> {
34 let mut count = WordCount::default();
35 for byte in file.bytes() {
36 let byte = byte?;
37 count.bytes += 1;
38 if byte == b' ' {
39 count.lines += 1;
40 }
41 }
42 Ok(count)
43 }
44
45 pub fn count_all(&self) -> Result<BTreeMap<PathBuf, WordCount>, Box<dyn Error>> {
46 let mut result = BTreeMap::new();
47 if self.files.len() == 0 {
48 let mut stdin = stdin();
49 result.insert(PathBuf::new(), self.count_file(&mut stdin)?);
50 } else {
51 for path in &self.files {
52 let file = File::open(path)?;
53 let mut reader = BufReader::new(file);
54 result.insert(path.clone(), self.count_file(&mut reader)?);
55 }
56 }
57
58 Ok(result)
59 }
60
61 pub fn run(&self) -> Result<(), Box<dyn Error>> {
62 let results = self.count_all()?;
63 for (file, counts) in results.iter() {
64 println!(
65 " {} {} {} {}",
66 counts.lines,
67 counts.words,
68 counts.bytes,
69 file.display()
70 );
71 }
72 Ok(())
73 }
74}
75
76impl Runnable for Wc {
77 fn run(&self) -> Result<(), Box<dyn Error>> {
78 self.run()
79 }
80}
81
82#[derive(Clone, Debug, Default)]
83pub struct WordCount {
84 bytes: u64,
85 chars: u64,
86 lines: u64,
87 words: u64,
88}