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
