My ‘word count’ version in Rust

Camilo MATAJIRA Avatar

This is my first small program in Rust. The idea was to implement a version of Linux’ wc command in Rust. Below is the code:

use std::io::Read;
use clap::Parser;
use std::fs;

/// Example: Options and flags
#[derive(Parser)]
struct Cli {
    /// Count words (-w, --words)
    #[clap(short, long, action)]
    words: bool,
    /// Count lines (-l, --lines)
    #[clap(short, long, action)]
    lines: bool,
    /// Count chars (-c, --chars)
    #[clap(short, long, action)]
    chars: bool,
    /// input_files, optional positional
    input_files: Option<Vec<String>>,
}

fn main() {
    let cli = Cli::parse();

    let mut buffer = String::new();
    match cli.input_files {
        Some(ref input_files) => {
            for i in input_files{
                let buffer_try = fs::read_to_string(i);
                match buffer_try {
                    Ok(buffer) => process_input(&cli, &buffer, &i),
                    Err(e) => {
                        match e.kind() {
                            std::io::ErrorKind::IsADirectory => println!("wc: {}: Is a directory", i),
                            _ => println!("Error: {} {}", i, e),
                        }
                    }
                }
            }
        }
        None => {
            std::io::stdin().read_to_string(&mut buffer).expect("paila");
            process_input(&cli, &buffer, "");
        }
    }
}
fn process_input(cli_args: &Cli, buffer: &String, filename: &str) {
    if !cli_args.words && !cli_args.lines && !cli_args.chars{
        let num_lines = count_lines(buffer);
        let num_words = count_words(buffer);
        let num_characters = count_characters(buffer);
        println!("  {:>5} {:>5} {:>5} {}", num_lines, num_words, num_characters, filename);
        return
    }
    let mut result = String::from(" ");
    if cli_args.words { 
        let num_lines = count_lines(buffer);
        result += &format!("{:>5} ", num_lines);
    }
    if cli_args.lines {
        let num_words = count_words(buffer);
        result += &format!("{:>5} ", num_words);
    }
    if cli_args.chars {
        let num_characters = count_characters(buffer);
        result += &format!("{:>5} ", num_characters);
    }
    result += &format!("{} ", filename);
    println!("{}", result);
}

fn count_lines(line: &String) -> usize {
    line.matches('\n').count()
}

fn count_words(line: &String) -> usize {
    line.split_whitespace().count()
}
fn count_characters(line: &String) -> usize {
    line.len()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_count_lines() {
        let test_1 = String::from("line 1\n");
        assert_eq!(count_lines(&test_1),1);
        let test_2 = String::from("line 1\nLine 2\n");
        assert_eq!(count_lines(&test_2),2);
    }
    #[test]
    fn test_count_words() {
        let test_1 = String::from("line 1\n");
        assert_eq!(count_words(&test_1),2);
        let test_2 = String::from("line 1 camilo andres\n");
        assert_eq!(count_words(&test_2),4);
    }
    #[test]
    fn test_count_characters() {
        let test_1 = String::from("line 1\n");
        assert_eq!(count_characters(&test_1),7);
    }
}

Below is the example usage:

cargo run ./Cargo.* -h
Example: Options and flags

Usage: wc [OPTIONS] [INPUT_FILES]...

Arguments:
  [INPUT_FILES]...  input_files, optional positional

Options:
  -w, --words  Count words (-w, --words)
  -l, --lines  Count lines (-l, --lines)
  -c, --chars  Count chars (-c, --chars)
  -h, --help   Print help
  
cargo run ./Cargo.*
    186   376  4899 ./Cargo.lock
      7    21   123 ./Cargo.toml

cargo run ./Cargo.* -l
   376 ./Cargo.lock
    21 ./Cargo.toml

cargo run ./Cargo.* -w
   186 ./Cargo.lock
     7 ./Cargo.toml

cargo run ./Cargo.* -c
  4899 ./Cargo.lock
   123 ./Cargo.toml

Tagged in :

Camilo MATAJIRA Avatar