From d9ad1bafdfde1ce60621c2be710716283b9379b1 Mon Sep 17 00:00:00 2001 From: Avril Date: Sat, 4 Jul 2020 18:11:49 +0100 Subject: [PATCH] read from stdin --- Cargo.toml | 6 +++ src/main.rs | 62 ++++++++++----------------- src/re.rs | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/text.rs | 39 +++++++++++++++++ 4 files changed, 184 insertions(+), 41 deletions(-) create mode 100644 src/re.rs create mode 100644 src/text.rs diff --git a/Cargo.toml b/Cargo.toml index 6744052..6a9ed44 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,12 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[profile.release] +opt-level = 3 +lto = "fat" +codegen-units = 1 +panic = "unwind" + [features] perl = ["pcre"] diff --git a/src/main.rs b/src/main.rs index 1e441f9..ec80323 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,56 +1,36 @@ -#[cfg(not(feature = "perl"))] -extern crate regex; +#![allow(dead_code)] -#[cfg(not(feature = "perl"))] -use regex::Regex; +mod re; +mod text; -#[cfg(not(feature = "perl"))] -fn main() { +fn main() -> Result<(), Box> +{ let args: Vec = std::env::args().collect(); if args.len() < 4 { println!("Usage: {} ", args[0]); + println!("Pass `-' as `' to read lines from stdin"); + std::process::exit(1); } else { - let re = Regex::new(&args[2]).unwrap(); + let re = re::Regex::compile(&args[2])?; let text = &args[1]; let group: usize = args[3].parse().expect("Invalid group number."); - match re.captures(&text) { - - Some(groups) => { - if group > groups.len() { - eprintln!("Invalid group number."); - } else { - println!("{}", groups.get(group).unwrap().as_str()); + if text == "-" { + text::stdin_lines(|text| -> Result { + match re.exec(&text)? { + Some(g) if g.len() > group => println!("{}", &g[group]), + _ => (), } - } - None => (), - } - } -} - -#[cfg(feature = "perl")] -extern crate pcre; - -#[cfg(feature = "perl")] -use pcre::Pcre; - -#[cfg(feature = "perl")] -fn main () { - let args: Vec = std::env::args().collect(); + Ok(true) + })?; + } else { - if args.len() < 4 { - println!("Usage: {} ", args[0]); - } else { - let mut re = Pcre::compile(&args[2]).expect("Invalid regex."); - let text = &args[1]; - let group: usize = args[3].parse().expect("Invalid group number."); - - match re.exec(&text) { - - Some(m) if m.string_count() > group => println!("{}", m.group(group)), - _ => (), + match re.exec(&text)? { + Some(g) if g.len() > group => println!("{}", &g[group]), + _ => (), + } } } + Ok(()) } - diff --git a/src/re.rs b/src/re.rs new file mode 100644 index 0000000..f0fa46e --- /dev/null +++ b/src/re.rs @@ -0,0 +1,118 @@ +#![allow(unused_imports)] + +use std::{ + error, + fmt::{ + self, + Write, + }, + sync::{ + Arc, + Mutex, + } +}; + +pub type Groups = Vec; + +pub struct Regex +{ + #[cfg(feature="perl")] + internal: Arc>, + #[cfg(not(feature = "perl"))] + internal: regex::Regex, +} + +#[derive(Debug)] +pub enum Error +{ + Compile(String), + Execute, + Internal, + Unknown, +} + +impl error::Error for Error{} +impl fmt::Display for Error +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + write!(f, "regex error: ")?; + match self { + Error::Compile(s) => write!(f, "compilation failed: {}", s), + Error::Execute => write!(f, "execution failed"), + Error::Internal => write!(f, "internal"), + _ => write!(f, "unknown"), + } + } +} + +impl Regex { + pub fn compile(string: impl AsRef) -> Result + { + #[cfg(feature = "perl")] + return Ok(Self{internal: Arc::new(Mutex::new(pcre::Pcre::compile(string.as_ref())?))}); + #[cfg(not(feature = "perl"))] + return Ok(Self{internal: regex::Regex::new(string.as_ref())?}); + } + + pub fn exec(&self, string: impl AsRef) -> Result, Error> + { + #[cfg(feature = "perl")] + return { + let mut re = self.internal.lock().unwrap(); + Ok(match re.exec(string.as_ref()) { + Some(m) => { + let len = m.string_count(); + let mut output = Vec::with_capacity(len); + for i in 0..len { + output.push(m.group(i).to_owned()); + } + Some(output) + }, + None => None, + }) + }; + #[cfg(not(feature = "perl"))] + return { + Ok(match self.internal.captures(string.as_ref()) { + Some(m) => { + let mut output = Vec::with_capacity(m.len()); + for i in 0..m.len() { + let ma = m.get(i).unwrap(); + let mut op = String::with_capacity(ma.range().len()); + write!(op, "{}", ma.as_str())?; + output.push(op); + } + Some(output) + }, + None => None, + }) + }; + } +} + +impl From for Error +{ + fn from(_er: fmt::Error) -> Self + { + Self::Internal + } +} + +#[cfg(not(feature = "perl"))] +impl From for Error +{ + fn from(er: regex::Error) -> Self + { + Self::Compile(format!("{}", er)) + } +} + +#[cfg(feature = "perl")] +impl From for Error +{ + fn from(er: pcre::CompilationError) -> Self + { + Self::Compile(format!("{}", er)) + } +} diff --git a/src/text.rs b/src/text.rs new file mode 100644 index 0000000..7ccc103 --- /dev/null +++ b/src/text.rs @@ -0,0 +1,39 @@ +use std::{ + io::{ + self, + Read, + }, + error::Error, + marker::{ + Send, Sync, + }, +}; + +/// Read whole stdin +pub fn stdin() -> io::Result +{ + let mut buffer = String::new(); + io::stdin().read_to_string(&mut buffer)?; + + Ok(buffer) +} + +/// Read each line with lambda +pub fn stdin_lines Result>(mut callback: F) -> io::Result + where E: Into> +{ + let mut lines=0; + let mut input = String::new(); + let stdin = io::stdin(); + while match stdin.read_line(&mut input) { + Ok(0) => return Ok(lines), + Err(e) => return Err(e), + Ok(v) if input.as_bytes()[v-1] == b'\n' => callback(&input[..v]).or_else(|e| Err(io::Error::new(io::ErrorKind::Other, e)))?, + Ok(_) => callback(&input[..]).or_else(|e| Err(io::Error::new(io::ErrorKind::Other, e)))?, + } { + input.clear(); + lines+=1; + } + + Ok(lines) +}