Dup Ver Goto 📝

MatchLines_001

PT2/lang/rust/examples does not exist
To
261 lines, 720 words, 5923 chars Page 'MatchLines_001' does not exist.

The aim here is to build a rudimentary grep like program, for the sake of learning the basics of Rust.

1 Simplest Viable Program

Find lines containing rain.

use std::fs::File;
use std::io::{self,BufRead};

fn main() {
    let result = proc();
    println!("Result: {:?}",result);
}

fn proc() -> io::Result<()> {
    let file = File::open("src.txt")?;
    let reader = io::BufReader::new(file);

    let pattern = "rain";
    for line in reader.lines() {
        if let Ok(l) = line {
            if l.contains(&pattern) {
                println!("{}: {}",&pattern,l);
            }
        }
    }

    Ok(())
}

2 Take pattern and files from command line

This is not robust: for example it will halt with an error on the first file-not-found or permission-denied. Anyway...

use std::fs::File;
use std::env;
use std::io::{self,BufRead};

// main is responsible for parsing command line args
// and sorting out parameters for proc()
fn main() {
    let args: Vec<String> = env::args().collect();
    let argc = args.len();
    if argc == 1 {
        println!("{} <pattern>",&args[0]);
        std::process::exit(1);
    }

    let result = proc(&args[1],Vec::from(&args[2..]));
    println!("Result: {:?}",result);
}

// proc() is the main procedure for the program.
// this iterates over provided files, and delegates
// the work for an individual file to procfile()
fn proc(pattern: &str, ifns: Vec<String>) -> io::Result<()> {
    for ifn in ifns {
        procfile(&pattern, &ifn)?;
    }
    Ok(())
}

// this 'greps' for 'pattern' in the file named 'ifn' (short for input file name)
fn procfile(pattern: &str, ifn: &str) -> io::Result<()> {
    let file = File::open(ifn)?;
    let reader = io::BufReader::new(file);

    for line in reader.lines() {
        if let Ok(l) = line {
            if l.contains(&pattern) {
                println!("{}: {}",&ifn,l);
            }
        }
    }

    Ok(())
}

3 Match Regular Expressions

This illustrates how to use an enum to combine multiple different types of error into a single error type, for use with Result.

cargo add regex
use std::fs::File;
use std::env;
use std::fmt;
use std::io::{self,BufRead};
use regex::Regex;

#[derive(Debug)]
enum MyError {
    IoError(io::Error),
    RegexError(regex::Error),
}

impl fmt::Display for MyError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            MyError::IoError(e) => write!(f, "IO error: {}", e),
            MyError::RegexError(e) => write!(f, "Regex error: {}", e),
        }
    }
}

impl From<io::Error> for MyError {
    fn from(error: io::Error) -> Self {
        MyError::IoError(error)
    }
}

impl From<regex::Error> for MyError {
    fn from(error: regex::Error) -> Self {
        MyError::RegexError(error)
    }
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let argc = args.len();
    if argc == 1 {
        println!("{} <pattern>",&args[0]);
        std::process::exit(1);
    }

    let result = proc(&args[1],Vec::from(&args[2..]));
    println!("Result: {:?}",result);
}

fn proc(pattern: &str, ifns: Vec<String>) -> Result<(),MyError> {
    for ifn in ifns {
        procfile(&pattern, &ifn)?;
    }
    Ok(())
}

fn procfile(pattern: &str, ifn: &str) -> Result<(),MyError> {
    let file = File::open(ifn)?;
    let reader = io::BufReader::new(file);
    let re = Regex::new(pattern)?;

    for line in reader.lines() {
        if let Ok(l) = line {
            if re.is_match(&l) {
                println!("{}: {}",&ifn,l);
            }
        }
    }

    Ok(())
}

4 More with Errors

use std::fs::File;
use std::io::{self,BufRead};
use std::env;
use std::fmt;
use regex::Regex;

#[derive(Debug)]
enum MyError {
    IoError(io::Error),
    RegexError(regex::Error),
}

impl fmt::Display for MyError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            MyError::IoError(e) => write!(f, "IO Error: {}", e),
            MyError::RegexError(e) => write!(f, "Regex Error: {}", e),
        }
    }
}

impl From<io::Error> for MyError {
    fn from(error: io::Error) -> Self {
       MyError::IoError(error)
    }
}

impl From<regex::Error> for MyError {
    fn from(error: regex::Error) -> Self {
        MyError::RegexError(error)
    }
}

fn main()  {
    let args: Vec<String> = env::args().collect();
    let argc = args.len();
    if argc < 3 {
        println!("{} pat file...",&args[0]);
        std::process::exit(1);
    }

    let result = proc(&args[1],&args[2..]);

    println!("Result: {:?}",result);
}

fn proc(pattern: &str, ifns: &[String]) -> Result<(),MyError> {
    let re = Regex::new(pattern)?;
    for ifn in ifns {
        match procfile(&re,&ifn) {
            Err(err) => {
                println!("Error with {}: {:?}",&ifn,&err);
            },
            Ok(_) => {
            }
        }
    }
    Ok(())
}

fn procfile(re: &Regex, ifn: &str) -> Result<(),MyError> {
    let file = File::open(ifn)?;
    let reader = io::BufReader::new(file);
    for line in reader.lines() {
        if let Ok(l) = line {
            if re.is_match(&l) {
                println!("{}: {}",ifn,l);
            }
        }
    }
    Ok(())
}

5 Colorise Output

cargo add colored
use colored::Colorize;
...

fn procfile(re: &Regex, ifn: &String) -> Result<(),MyError> {
    let file = std::fs::File::open(ifn)?;
    let reader = std::io::BufReader::new(file);
    for line in reader.lines() {
        if let Ok(l) = line {
            if let Some(m) = re.find(&l) {
                let i = m.start();
                let j = m.end();
                let left = &l[0..i];
                let mid = &l[i..j];
                let right = &l[j..];
                println!("{}: {}{}{}",&ifn,&left,&mid.red().bold(),&right);
            }
        }
    }
    Ok(())
}