difftreelog
feat feature-gate regex
in: master
3 files changed
cmds/jrsonnet/Cargo.tomldiffbeforeafterboth--- a/cmds/jrsonnet/Cargo.toml
+++ b/cmds/jrsonnet/Cargo.toml
@@ -30,9 +30,7 @@
# Bigint type
exp-bigint = ["jrsonnet-evaluator/exp-bigint", "jrsonnet-cli/exp-bigint"]
# std.regex and co.
-exp-regex = [
- "jrsonnet-stdlib/exp-regex",
-]
+exp-regex = ["jrsonnet-cli/exp-regex"]
# obj?.field, obj?.['field']
exp-null-coaelse = [
"jrsonnet-evaluator/exp-null-coaelse",
crates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/typed/conversions.rs
+++ b/crates/jrsonnet-evaluator/src/typed/conversions.rs
@@ -10,7 +10,7 @@
bail,
function::{native::NativeDesc, FuncDesc, FuncVal},
typed::CheckType,
- val::{IndexableVal, ThunkMapper},
+ val::{IndexableVal, StrValue, ThunkMapper},
ObjValue, ObjValueBuilder, Result, Thunk, Val,
};
crates/jrsonnet-stdlib/src/regex.rsdiffbeforeafterboth1use std::{cell::RefCell, hash::BuildHasherDefault, num::NonZeroUsize, rc::Rc};23use ::regex::Regex;4use jrsonnet_evaluator::{5 error::{ErrorKind::*, Result},6 val::StrValue,7 IStr, ObjValueBuilder, Val,8};9use jrsonnet_macros::builtin;10use lru::LruCache;11use rustc_hash::FxHasher;1213pub struct RegexCacheInner {14 cache: RefCell<LruCache<IStr, Rc<Regex>, BuildHasherDefault<FxHasher>>>,15}16impl Default for RegexCacheInner {17 fn default() -> Self {18 Self {19 cache: RefCell::new(LruCache::with_hasher(20 NonZeroUsize::new(20).unwrap(),21 BuildHasherDefault::default(),22 )),23 }24 }25}26pub type RegexCache = Rc<RegexCacheInner>;27impl RegexCacheInner {28 fn parse(&self, pattern: IStr) -> Result<Rc<Regex>> {29 let mut cache = self.cache.borrow_mut();30 if let Some(found) = cache.get(&pattern) {31 return Ok(found.clone());32 }33 let regex = Regex::new(&pattern)34 .map_err(|e| RuntimeError(format!("regex parse failed: {e}").into()))?;35 let regex = Rc::new(regex);36 cache.push(pattern, regex.clone());37 Ok(regex)38 }39}4041pub fn regex_match_inner(regex: &Regex, str: String) -> Result<Val> {42 let mut out = ObjValueBuilder::with_capacity(3);4344 let mut captures = Vec::with_capacity(regex.captures_len());45 let mut named_captures = ObjValueBuilder::with_capacity(regex.capture_names().len());4647 let Some(captured) = regex.captures(&str) else {48 return Ok(Val::Null)49 };5051 for ele in captured.iter().skip(1) {52 if let Some(ele) = ele {53 captures.push(Val::Str(StrValue::Flat(ele.as_str().into())))54 } else {55 captures.push(Val::Str(StrValue::Flat(IStr::empty())))56 }57 }58 for (i, name) in regex59 .capture_names()60 .skip(1)61 .enumerate()62 .flat_map(|(i, v)| Some((i, v?)))63 {64 let capture = captures[i].clone();65 named_captures.member(name.into()).value(capture)?;66 }6768 out.member("string".into())69 .value_unchecked(Val::Str(captured.get(0).unwrap().as_str().into()));70 out.member("captures".into())71 .value_unchecked(Val::Arr(captures.into()));72 out.member("namedCaptures".into())73 .value_unchecked(Val::Obj(named_captures.build()));7475 Ok(Val::Obj(out.build()))76}7778#[builtin(fields(79 cache: RegexCache,80))]81pub fn builtin_regex_partial_match(82 this: &builtin_regex_partial_match,83 pattern: IStr,84 str: String,85) -> Result<Val> {86 let regex = this.cache.parse(pattern)?;87 regex_match_inner(®ex, str)88}8990#[builtin(fields(91 cache: RegexCache,92))]93pub fn builtin_regex_full_match(94 this: &builtin_regex_full_match,95 pattern: StrValue,96 str: String,97) -> Result<Val> {98 let pattern = format!("^{pattern}$").into();99 let regex = this.cache.parse(pattern)?;100 regex_match_inner(®ex, str)101}102103#[builtin]104pub fn builtin_regex_quote_meta(pattern: String) -> String {105 regex::escape(&pattern)106}107108#[builtin(fields(109 cache: RegexCache,110))]111pub fn builtin_regex_replace(112 this: &builtin_regex_replace,113 str: String,114 pattern: IStr,115 to: String,116) -> Result<String> {117 let regex = this.cache.parse(pattern)?;118 let replaced = regex.replace(&str, to);119 Ok(replaced.to_string())120}121122#[builtin(fields(123 cache: RegexCache,124))]125pub fn builtin_regex_global_replace(126 this: &builtin_regex_global_replace,127 str: String,128 pattern: IStr,129 to: String,130) -> Result<String> {131 let regex = this.cache.parse(pattern)?;132 let replaced = regex.replace_all(&str, to);133 Ok(replaced.to_string())134}1use std::{cell::RefCell, hash::BuildHasherDefault, num::NonZeroUsize, rc::Rc};23use ::regex::Regex;4use jrsonnet_evaluator::{5 error::{ErrorKind::*, Result},6 val::StrValue,7 IStr, ObjValueBuilder, Val,8};9use jrsonnet_macros::builtin;10use lru::LruCache;11use rustc_hash::FxHasher;1213pub struct RegexCacheInner {14 cache: RefCell<LruCache<IStr, Rc<Regex>, BuildHasherDefault<FxHasher>>>,15}16impl Default for RegexCacheInner {17 fn default() -> Self {18 Self {19 cache: RefCell::new(LruCache::with_hasher(20 NonZeroUsize::new(20).unwrap(),21 BuildHasherDefault::default(),22 )),23 }24 }25}26pub type RegexCache = Rc<RegexCacheInner>;27impl RegexCacheInner {28 fn parse(&self, pattern: IStr) -> Result<Rc<Regex>> {29 let mut cache = self.cache.borrow_mut();30 if let Some(found) = cache.get(&pattern) {31 return Ok(found.clone());32 }33 let regex = Regex::new(&pattern)34 .map_err(|e| RuntimeError(format!("regex parse failed: {e}").into()))?;35 let regex = Rc::new(regex);36 cache.push(pattern, regex.clone());37 Ok(regex)38 }39}4041pub fn regex_match_inner(regex: &Regex, str: String) -> Result<Val> {42 let mut out = ObjValueBuilder::with_capacity(3);4344 let mut captures = Vec::with_capacity(regex.captures_len());45 let mut named_captures = ObjValueBuilder::with_capacity(regex.capture_names().len());4647 let Some(captured) = regex.captures(&str) else {48 return Ok(Val::Null);49 };5051 for ele in captured.iter().skip(1) {52 if let Some(ele) = ele {53 captures.push(Val::Str(StrValue::Flat(ele.as_str().into())))54 } else {55 captures.push(Val::Str(StrValue::Flat(IStr::empty())))56 }57 }58 for (i, name) in regex59 .capture_names()60 .skip(1)61 .enumerate()62 .flat_map(|(i, v)| Some((i, v?)))63 {64 let capture = captures[i].clone();65 named_captures.field(name).try_value(capture)?;66 }6768 out.field("string")69 .value(Val::Str(captured.get(0).unwrap().as_str().into()));70 out.field("captures").value(Val::Arr(captures.into()));71 out.field("namedCaptures")72 .value(Val::Obj(named_captures.build()));7374 Ok(Val::Obj(out.build()))75}7677#[builtin(fields(78 cache: RegexCache,79))]80pub fn builtin_regex_partial_match(81 this: &builtin_regex_partial_match,82 pattern: IStr,83 str: String,84) -> Result<Val> {85 let regex = this.cache.parse(pattern)?;86 regex_match_inner(®ex, str)87}8889#[builtin(fields(90 cache: RegexCache,91))]92pub fn builtin_regex_full_match(93 this: &builtin_regex_full_match,94 pattern: StrValue,95 str: String,96) -> Result<Val> {97 let pattern = format!("^{pattern}$").into();98 let regex = this.cache.parse(pattern)?;99 regex_match_inner(®ex, str)100}101102#[builtin]103pub fn builtin_regex_quote_meta(pattern: String) -> String {104 regex::escape(&pattern)105}106107#[builtin(fields(108 cache: RegexCache,109))]110pub fn builtin_regex_replace(111 this: &builtin_regex_replace,112 str: String,113 pattern: IStr,114 to: String,115) -> Result<String> {116 let regex = this.cache.parse(pattern)?;117 let replaced = regex.replace(&str, to);118 Ok(replaced.to_string())119}120121#[builtin(fields(122 cache: RegexCache,123))]124pub fn builtin_regex_global_replace(125 this: &builtin_regex_global_replace,126 str: String,127 pattern: IStr,128 to: String,129) -> Result<String> {130 let regex = this.cache.parse(pattern)?;131 let replaced = regex.replace_all(&str, to);132 Ok(replaced.to_string())133}