Skip to main content

graphix_package_re/
lib.rs

1#![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}