From a87d85d709dbc4539fe18f23a6a067e3b4e64f54 Mon Sep 17 00:00:00 2001 From: Eduardo Cuducos <4732915+cuducos@users.noreply.github.com> Date: Sat, 31 Aug 2024 14:28:31 -0400 Subject: [PATCH] Removes regex dependency --- Cargo.lock | 45 -------------------- Cargo.toml | 1 - src/parser.rs | 116 ++++++++++++++++++++++++++++++++++++-------------- 3 files changed, 85 insertions(+), 77 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 080272d..bb71ad6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,15 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - [[package]] name = "anstream" version = "0.6.15" @@ -118,7 +109,6 @@ dependencies = [ "anyhow", "clap", "rand", - "regex", ] [[package]] @@ -144,12 +134,6 @@ version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" -[[package]] -name = "memchr" -version = "2.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" - [[package]] name = "ppv-lite86" version = "0.2.20" @@ -207,35 +191,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "regex" -version = "1.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" - [[package]] name = "strsim" version = "0.11.1" diff --git a/Cargo.toml b/Cargo.toml index 3394497..0216f42 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,4 +7,3 @@ edition = "2021" anyhow = "1.0.86" clap = "4.5.16" rand = "0.8.5" -regex = "1.10.6" diff --git a/src/parser.rs b/src/parser.rs index 8af5b25..12cf655 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -6,14 +6,12 @@ use std::{ use anyhow::Result; use rand::{thread_rng, Rng}; -use regex::Regex; use crate::model::{AutoGeneratedVariable, Block, Comment, SimpleVariable, VariableType}; -const NAME_PATTERN: &str = r"^[A-Z0-9_]+$"; -const RANDOM_VARIABLE_PATTERN: &str = r"\\d*))?\>"; -const AUTO_GENERATED_PATTERN: &str = r"\{[A-Z0-9_]+\}"; - +const FIRST_CHAR: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +const NAME_CHARS: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; +const RANDOM_VARIABLE_PREFIX: &str = " bool { + match name.chars().next() { + Some(c) => { + if !FIRST_CHAR.contains(c) { + return false; + } + } + None => return false, + } + for c in name.chars() { + if !NAME_CHARS.contains(c) { + return false; + } + } + true +} + +fn is_auto_generated_variable(value: &str) -> bool { + if let Some(first) = value.find('{') { + if let Some(second) = value[first + 1..].find('}') { + let name = &value[first + 1..first + second]; + return is_valid_name(name); + } + } + false +} + +fn is_random_variable(value: &str) -> (bool, Option) { + if let Some(rest) = value.strip_prefix(RANDOM_VARIABLE_PREFIX) { + if let Some(number) = rest.strip_suffix('>') { + if number.is_empty() { + return (true, None); + } else if !number.starts_with(':') { + return (false, None); + } else if let Ok(n) = number[1..].parse() { + return (true, Some(n)); + } + } + } + (false, None) +} + enum Expecting { Title, DescriptionOrVariables, @@ -54,9 +94,6 @@ pub struct Parser { path: String, random_chars: String, use_default: bool, - name_pattern: Regex, - random_pattern: Regex, - auto_generated_pattern: Regex, state: Expecting, buffer: Option, pub blocks: Vec, @@ -68,9 +105,6 @@ impl Parser { path: path.to_string(), random_chars: random_chars.to_string(), use_default: *use_default, - name_pattern: Regex::new(NAME_PATTERN)?, - random_pattern: Regex::new(RANDOM_VARIABLE_PATTERN)?, - auto_generated_pattern: Regex::new(AUTO_GENERATED_PATTERN)?, state: Expecting::Title, buffer: None, blocks: vec![], @@ -82,38 +116,31 @@ impl Parser { name: &str, description: Option<&str>, value: &str, - ) -> Result { - if let Some(matches) = self.random_pattern.captures(value) { + ) -> Option { + let (is_random, size) = is_random_variable(value); + if is_random { let mut rng = thread_rng(); - let length = matches - .name("size") - .map(|m| m.as_str().parse::()) - .transpose()? - .unwrap_or(rng.gen_range(64..=128)); + let length = size.unwrap_or(rng.gen_range(64..=128)); let max_chars_idx = self.random_chars.chars().count(); let mut value: String = String::from(""); for _ in 0..length { let pos = rng.gen_range(0..max_chars_idx); value.push(self.random_chars.chars().nth(pos).unwrap()) } - Ok(SimpleVariable::new(name, Some(value.as_str()), description)) - } else { - Err(anyhow::anyhow!("Invalid random variable: {}", value)) + return Some(SimpleVariable::new(name, Some(value.as_str()), description)); } + None } fn parse_auto_generated_variable( &self, name: &str, value: &str, - ) -> Result { - if self.auto_generated_pattern.find(value).is_some() { - return Ok(AutoGeneratedVariable::new(name, value)); + ) -> Option { + if is_auto_generated_variable(value) { + return Some(AutoGeneratedVariable::new(name, value)); } - Err(anyhow::anyhow!( - "Invalid auto-generated variable: {}", - value - )) + None } fn parse_variable(&self, pos: usize, line: &str) -> Result { @@ -123,7 +150,7 @@ impl Parser { line, HELP_VARIABLE ))?; - if !self.name_pattern.is_match(name) { + if !is_valid_name(name) { return Err(anyhow::anyhow!( "Invalid variable name on line {}: {}\nHint :{}", pos, @@ -139,10 +166,10 @@ impl Parser { if val.is_empty() { default = None; } else { - if let Ok(v) = self.parse_random_variable(name, description, val) { + if let Some(v) = self.parse_random_variable(name, description, val) { return Ok(VariableType::Input(v)); } - if let Ok(v) = self.parse_auto_generated_variable(name, val) { + if let Some(v) = self.parse_auto_generated_variable(name, val) { return Ok(VariableType::AutoGenerated(v)); } } @@ -250,6 +277,33 @@ mod tests { use super::*; use crate::DEFAULT_RANDOM_CHARS; + #[test] + fn test_is_valid_name() { + assert!(is_valid_name("HELLO_WORLD")); + assert!(is_valid_name("HELLO_WORLD_42")); + assert!(!is_valid_name("42HELLO")); + assert!(!is_valid_name("Hello World")); + assert!(!is_valid_name("HELLO-WORLD")); + } + + #[test] + fn test_is_auto_generated_variable() { + assert!(!is_auto_generated_variable("42")); + assert!(!is_auto_generated_variable("Hello, world!")); + assert!(!is_auto_generated_variable("Hello, {world}!")); + assert!(is_auto_generated_variable("Hello, {WORLD}!")); + } + + #[test] + fn test_is_random_variable() { + assert!(!is_random_variable("random:42").0); + assert_eq!(is_random_variable("random:42").1, None); + assert!(is_random_variable("").0); + assert_eq!(is_random_variable("").1, Some(42)); + assert!(is_random_variable("").0); + assert_eq!(is_random_variable("").1, None); + } + #[test] fn test_parser() { let sample = PathBuf::from(env!("CARGO_MANIFEST_DIR"))