diff --git a/CHANGELOG.md b/CHANGELOG.md index a8266cd..796ed00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/). - `state` command to print the current poll/listen variable state. - `m` as another duration unit for minute. +- `std::regex` library for regex matching. ### Fixed diff --git a/Cargo.lock b/Cargo.lock index 9fb1a67..91c7f69 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1843,6 +1843,7 @@ dependencies = [ "log", "nix", "once_cell", + "regex", "rhai", "rhai_trace", "serde", diff --git a/crates/rhai_impl/Cargo.toml b/crates/rhai_impl/Cargo.toml index a09091d..283526e 100644 --- a/crates/rhai_impl/Cargo.toml +++ b/crates/rhai_impl/Cargo.toml @@ -23,4 +23,5 @@ nix = { workspace = true, features = ["process", "fs", "signal"] } libc.workspace = true # error handling rhai_trace = "0.2.0" -codespan-reporting.workspace = true \ No newline at end of file +codespan-reporting.workspace = true +regex.workspace = true \ No newline at end of file diff --git a/crates/rhai_impl/src/providers/stdlib/mod.rs b/crates/rhai_impl/src/providers/stdlib/mod.rs index 05f075b..94f1ca2 100644 --- a/crates/rhai_impl/src/providers/stdlib/mod.rs +++ b/crates/rhai_impl/src/providers/stdlib/mod.rs @@ -1,23 +1,28 @@ pub mod command; pub mod env; pub mod monitor; +pub mod regex; pub mod text; use rhai::exported_module; use rhai::module_resolvers::StaticModuleResolver; pub fn register_stdlib(resolver: &mut StaticModuleResolver) { - use crate::providers::stdlib::{command::command, env::env, monitor::monitor, text::text}; + use crate::providers::stdlib::{ + command::command, env::env, monitor::monitor, regex::regex_lib, text::text, + }; // adding modules let text_mod = exported_module!(text); let env_mod = exported_module!(env); let monitor_mod = exported_module!(monitor); let command_mod = exported_module!(command); + let regex_mod = exported_module!(regex_lib); // inserting modules resolver.insert("std::text", text_mod); resolver.insert("std::env", env_mod); resolver.insert("std::monitor", monitor_mod); resolver.insert("std::command", command_mod); + resolver.insert("std::regex", regex_mod); } diff --git a/crates/rhai_impl/src/providers/stdlib/regex.rs b/crates/rhai_impl/src/providers/stdlib/regex.rs new file mode 100644 index 0000000..f746fa0 --- /dev/null +++ b/crates/rhai_impl/src/providers/stdlib/regex.rs @@ -0,0 +1,124 @@ +use regex::Regex; +use rhai::plugin::*; +use rhai::{Array, Dynamic}; + +/// A module providing regular expression support. +#[export_module] +pub mod regex_lib { + /// Checks if a regex pattern matches the given text. + /// + /// # Arguments + /// + /// * `text` - A string to be matched with the pattern. + /// * `pattern` - The pattern to match on the string. + /// + /// # Returns + /// + /// Returns a boolean (true/false) based on if the pattern is matched on the text provided. + /// + /// # Example + /// + /// ```javascript + /// import "std::regex" as regex; + /// + /// let result = regex::is_match("This is an example!", "example"); + /// if result == true { + /// print("The pattern is matched!"); + /// } + /// ``` + #[rhai_fn(return_raw)] + pub fn is_match(text: &str, pattern: &str) -> Result> { + let re = Regex::new(pattern).map_err(|e| format!("Failed to read regex pattern: {}", e))?; + Ok(re.is_match(text)) + } + + /// Returns the first match of a regex pattern in the text. + /// + /// # Arguments + /// + /// * `text` - A string to be matched with the pattern. + /// * `pattern` - The pattern to match on the string. + /// + /// # Returns + /// + /// Returns a string which is the first match of a regex pattern. + /// + /// # Example + /// + /// ```javascript + /// import "std::regex" as regex; + /// + /// let result = regex::find("This is an example!", `\be\w*\b`); + /// print(result); // output: "example" + /// ``` + #[rhai_fn(return_raw)] + pub fn find(text: &str, pattern: &str) -> Result> { + let re = Regex::new(pattern).map_err(|e| format!("Failed to read regex pattern: {}", e))?; + match re.find(text).map(|m| m.as_str().to_string()) { + Some(s) => Ok(s), + None => Ok(String::new()), + } + } + + /// Returns all matches of a regex pattern in the text. + /// + /// # Arguments + /// + /// * `text` - A string to be matched with the pattern. + /// * `pattern` - The pattern to match on the string. + /// + /// # Returns + /// + /// Returns aan array of strings containing the all the things that match the regex pattern in the provided text. + /// + /// # Example + /// + /// # Example + /// + /// ```javascript + /// import "std::regex" as regex; + /// + /// let result = regex::find("This is an example!", `\be\w*\b`); + /// print(result); // output: ["example"] + /// ``` + #[rhai_fn(return_raw)] + pub fn find_all(text: &str, pattern: &str) -> Result> { + let re = Regex::new(pattern).map_err(|e| format!("Failed to read regex pattern: {}", e))?; + let results: Array = re + .find_iter(text) + .map(|m| Dynamic::from(m.as_str().to_string())) + .collect(); + + Ok(results) + } + + /// Replaces all matches of a regex pattern with a replacement string. + /// + /// # Arguments + /// + /// * `text` - A string to be matched with the pattern. + /// * `pattern` - The pattern to match on the string. + /// * `replacement` - A string that the matched pattern will get replaced with. + /// + /// # Returns + /// + /// Returns the provided text with the matches of the regex pattern replaced with the provided replacement argument. + /// + /// # Example + /// + /// ```javascript + /// import "std::regex" as regex; + /// + /// let result = regex::replace("This is an example!", "an example", "a test"); + /// print(result); // output: "This is a test!" + /// ``` + #[rhai_fn(return_raw)] + pub fn replace( + text: &str, + pattern: &str, + replacement: &str, + ) -> Result> { + let re = Regex::new(pattern).map_err(|e| format!("Failed to read regex pattern: {}", e))?; + Ok(re.replace_all(text, replacement).to_string()) + } +}