rustutils_wc/
lib.rs

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/// Print newline, word, and byte counts for each file.
10#[derive(Parser, Clone, Debug)]
11#[clap(author, version, about, long_about = None)]
12pub struct Wc {
13    /// Print the byte counts
14    #[clap(long, short)]
15    pub bytes: bool,
16    /// Print the character counts (input is interpreted as UTF-8).
17    #[clap(long, short)]
18    pub chars: bool,
19    /// Print the line count.
20    #[clap(long, short)]
21    pub lines: bool,
22    /// Print the word count.
23    #[clap(long, short)]
24    pub words: bool,
25    /// Output result as JSON.
26    #[clap(long, short)]
27    pub json: bool,
28    /// Files to read, if not specified, standard input is read.
29    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}