graphix_package_re/
lib.rs1#![doc(
2 html_logo_url = "https://graphix-lang.github.io/graphix/graphix-icon.svg",
3 html_favicon_url = "https://graphix-lang.github.io/graphix/graphix-icon.svg"
4)]
5use anyhow::Result;
6use arcstr::{literal, ArcStr};
7use graphix_compiler::errf;
8use graphix_package_core::{deftype, CachedArgs, CachedVals, EvalCached};
9use netidx::subscriber::Value;
10use netidx_value::ValArray;
11use regex::Regex;
12
13fn maybe_compile(s: &str, re: &mut Option<Regex>) -> Result<()> {
14 let compile = match re {
15 None => true,
16 Some(re) => re.as_str() != s,
17 };
18 if compile {
19 *re = Some(Regex::new(s)?)
20 }
21 Ok(())
22}
23
24static TAG: ArcStr = literal!("ReError");
25
26#[derive(Debug, Default)]
27struct IsMatchEv {
28 re: Option<Regex>,
29}
30
31impl EvalCached for IsMatchEv {
32 const NAME: &str = "re_is_match";
33 deftype!("fn(#pat:string, string) -> Result<bool, `ReError(string)>");
34
35 fn eval(&mut self, from: &CachedVals) -> Option<Value> {
36 if let Some(Value::String(s)) = &from.0[0] {
37 if let Err(e) = maybe_compile(s, &mut self.re) {
38 return Some(errf!(TAG, "{e:?}"));
39 }
40 }
41 if let Some(Value::String(s)) = &from.0[1] {
42 if let Some(re) = self.re.as_ref() {
43 return Some(Value::Bool(re.is_match(s)));
44 }
45 }
46 None
47 }
48}
49
50type IsMatch = CachedArgs<IsMatchEv>;
51
52#[derive(Debug, Default)]
53struct FindEv {
54 re: Option<Regex>,
55}
56
57impl EvalCached for FindEv {
58 const NAME: &str = "re_find";
59 deftype!("fn(#pat:string, string) -> Result<Array<string>, `ReError(string)>");
60
61 fn eval(&mut self, from: &CachedVals) -> Option<Value> {
62 if let Some(Value::String(s)) = &from.0[0] {
63 if let Err(e) = maybe_compile(s, &mut self.re) {
64 return Some(errf!(TAG, "{e:?}"));
65 }
66 }
67 if let Some(Value::String(s)) = &from.0[1] {
68 if let Some(re) = self.re.as_ref() {
69 let a = ValArray::from_iter(
70 re.find_iter(s).map(|s| Value::String(s.as_str().into())),
71 );
72 return Some(Value::Array(a));
73 }
74 }
75 None
76 }
77}
78
79type Find = CachedArgs<FindEv>;
80
81#[derive(Debug, Default)]
82struct CapturesEv {
83 re: Option<Regex>,
84}
85
86impl EvalCached for CapturesEv {
87 const NAME: &str = "re_captures";
88 deftype!("fn(#pat:string, string) -> Result<Array<Array<Option<string>>>, `ReError(string)>");
89
90 fn eval(&mut self, from: &CachedVals) -> Option<Value> {
91 if let Some(Value::String(s)) = &from.0[0] {
92 if let Err(e) = maybe_compile(s, &mut self.re) {
93 return Some(errf!(TAG, "{e:?}"));
94 }
95 }
96 if let Some(Value::String(s)) = &from.0[1] {
97 if let Some(re) = self.re.as_ref() {
98 let a = ValArray::from_iter(re.captures_iter(s).map(|c| {
99 let a = ValArray::from_iter(c.iter().map(|m| match m {
100 None => Value::Null,
101 Some(m) => Value::String(m.as_str().into()),
102 }));
103 Value::Array(a)
104 }));
105 return Some(Value::Array(a));
106 }
107 }
108 None
109 }
110}
111
112type Captures = CachedArgs<CapturesEv>;
113
114#[derive(Debug, Default)]
115struct SplitEv {
116 re: Option<Regex>,
117}
118
119impl EvalCached for SplitEv {
120 const NAME: &str = "re_split";
121 deftype!("fn(#pat:string, string) -> Result<Array<string>, `ReError(string)>");
122
123 fn eval(&mut self, from: &CachedVals) -> Option<Value> {
124 if let Some(Value::String(s)) = &from.0[0] {
125 if let Err(e) = maybe_compile(s, &mut self.re) {
126 return Some(errf!(TAG, "{e:?}"));
127 }
128 }
129 if let Some(Value::String(s)) = &from.0[1] {
130 if let Some(re) = self.re.as_ref() {
131 let a = ValArray::from_iter(re.split(s).map(|s| Value::String(s.into())));
132 return Some(Value::Array(a));
133 }
134 }
135 None
136 }
137}
138
139type Split = CachedArgs<SplitEv>;
140
141#[derive(Debug, Default)]
142struct SplitNEv {
143 re: Option<Regex>,
144 lim: Option<usize>,
145}
146
147impl EvalCached for SplitNEv {
148 const NAME: &str = "re_splitn";
149 deftype!(
150 "fn(#pat:string, #limit:i64, string) -> Result<Array<string>, `ReError(string)>"
151 );
152
153 fn eval(&mut self, from: &CachedVals) -> Option<Value> {
154 if let Some(Value::String(s)) = &from.0[0] {
155 if let Err(e) = maybe_compile(s, &mut self.re) {
156 return Some(errf!(TAG, "{e:?}"));
157 }
158 }
159 if let Some(Value::I64(lim)) = &from.0[1] {
160 self.lim = Some(*lim as usize);
161 }
162 if let Some(Value::String(s)) = &from.0[2] {
163 if let Some(lim) = self.lim {
164 if let Some(re) = self.re.as_ref() {
165 let a = ValArray::from_iter(
166 re.splitn(s, lim).map(|s| Value::String(s.into())),
167 );
168 return Some(Value::Array(a));
169 }
170 }
171 }
172 None
173 }
174}
175
176type SplitN = CachedArgs<SplitNEv>;
177
178#[cfg(test)]
179mod test;
180
181graphix_derive::defpackage! {
182 builtins => [
183 IsMatch,
184 Find,
185 Captures,
186 Split,
187 SplitN,
188 ],
189}