artichoke_backend/extn/core/regexp/
trampoline.rs

1use crate::convert::{implicitly_convert_to_int, implicitly_convert_to_nilable_string, implicitly_convert_to_string};
2use crate::extn::core::regexp::Regexp;
3use crate::extn::core::symbol::Symbol;
4use crate::extn::prelude::*;
5
6pub fn initialize(
7    interp: &mut Artichoke,
8    pattern: Value,
9    options: Option<Value>,
10    encoding: Option<Value>,
11    mut into: Value,
12) -> Result<Value, Error> {
13    if let Ok(existing) = unsafe { Regexp::unbox_from_value(&mut into, interp) } {
14        if existing.is_literal() {
15            return Err(FrozenError::with_message("can't modify literal regexp").into());
16        }
17        return Err(TypeError::with_message("already initialized regexp").into());
18    }
19    let (options, encoding) = interp.try_convert_mut((options, encoding))?;
20    let regexp = Regexp::initialize(interp, pattern, options, encoding)?;
21    let mut value = Regexp::box_into_value(regexp, into, interp)?;
22    if matches!(options, Some(options) if options.is_literal()) {
23        value.freeze(interp)?;
24    }
25    Ok(value)
26}
27
28pub fn escape(interp: &mut Artichoke, mut pattern: Value) -> Result<Value, Error> {
29    let pattern_vec = if let Ruby::Symbol = pattern.ruby_type() {
30        let symbol = unsafe { Symbol::unbox_from_value(&mut pattern, interp)? };
31        symbol.bytes(interp).to_vec()
32    } else {
33        // SAFETY: Convert the bytes to an owned vec to prevent the underlying
34        // `RString*` backing `pattern` from being freed during a garbage
35        // collection.
36        unsafe { implicitly_convert_to_string(interp, &mut pattern)?.to_vec() }
37    };
38    let pattern = Regexp::escape(&pattern_vec)?;
39    interp.try_convert_mut(pattern)
40}
41
42pub fn union<T>(interp: &mut Artichoke, patterns: T) -> Result<Value, Error>
43where
44    T: IntoIterator<Item = Value>,
45{
46    let regexp = Regexp::union(interp, patterns)?;
47    Regexp::alloc_value(regexp, interp)
48}
49
50pub fn is_match(
51    interp: &mut Artichoke,
52    mut regexp: Value,
53    mut pattern: Value,
54    pos: Option<Value>,
55) -> Result<Value, Error> {
56    let regexp = unsafe { Regexp::unbox_from_value(&mut regexp, interp)? };
57    let pattern = unsafe { implicitly_convert_to_nilable_string(interp, &mut pattern)? };
58    let pos = if let Some(pos) = pos {
59        Some(implicitly_convert_to_int(interp, pos)?)
60    } else {
61        None
62    };
63    let is_match = regexp.is_match(pattern, pos)?;
64    Ok(interp.convert(is_match))
65}
66
67pub fn match_(
68    interp: &mut Artichoke,
69    mut regexp: Value,
70    mut pattern: Value,
71    pos: Option<Value>,
72    block: Option<Block>,
73) -> Result<Value, Error> {
74    let regexp = unsafe { Regexp::unbox_from_value(&mut regexp, interp)? };
75    let pattern_vec;
76    let pattern = if let Ruby::Symbol = pattern.ruby_type() {
77        let symbol = unsafe { Symbol::unbox_from_value(&mut pattern, interp)? };
78        pattern_vec = symbol.bytes(interp).to_vec();
79        Some(pattern_vec.as_slice())
80    } else {
81        unsafe { implicitly_convert_to_nilable_string(interp, &mut pattern)? }
82    };
83    let pos = if let Some(pos) = pos {
84        Some(implicitly_convert_to_int(interp, pos)?)
85    } else {
86        None
87    };
88    regexp.match_(interp, pattern, pos, block)
89}
90
91pub fn eql(interp: &mut Artichoke, mut regexp: Value, other: Value) -> Result<Value, Error> {
92    let regexp = unsafe { Regexp::unbox_from_value(&mut regexp, interp)? };
93    let cmp = regexp.eql(interp, other);
94    Ok(interp.convert(cmp))
95}
96
97pub fn case_compare(interp: &mut Artichoke, mut regexp: Value, other: Value) -> Result<Value, Error> {
98    let regexp = unsafe { Regexp::unbox_from_value(&mut regexp, interp)? };
99    let cmp = regexp.case_compare(interp, other)?;
100    Ok(interp.convert(cmp))
101}
102
103pub fn match_operator(interp: &mut Artichoke, mut regexp: Value, mut pattern: Value) -> Result<Value, Error> {
104    let regexp = unsafe { Regexp::unbox_from_value(&mut regexp, interp)? };
105    let pattern_vec;
106    let pattern = if let Ruby::Symbol = pattern.ruby_type() {
107        let symbol = unsafe { Symbol::unbox_from_value(&mut pattern, interp)? };
108        pattern_vec = symbol.bytes(interp).to_vec();
109        Some(pattern_vec.as_slice())
110    } else {
111        unsafe { implicitly_convert_to_nilable_string(interp, &mut pattern)? }
112    };
113    let pos = regexp.match_operator(interp, pattern)?;
114    match pos.map(i64::try_from) {
115        Some(Ok(pos)) => Ok(interp.convert(pos)),
116        Some(Err(_)) => Err(ArgumentError::with_message("string too long").into()),
117        None => Ok(Value::nil()),
118    }
119}
120
121pub fn is_casefold(interp: &mut Artichoke, mut regexp: Value) -> Result<Value, Error> {
122    let regexp = unsafe { Regexp::unbox_from_value(&mut regexp, interp)? };
123    let is_casefold = regexp.is_casefold();
124    Ok(interp.convert(is_casefold))
125}
126
127pub fn is_fixed_encoding(interp: &mut Artichoke, mut regexp: Value) -> Result<Value, Error> {
128    let regexp = unsafe { Regexp::unbox_from_value(&mut regexp, interp)? };
129    let is_fixed_encoding = regexp.is_fixed_encoding();
130    Ok(interp.convert(is_fixed_encoding))
131}
132
133pub fn hash(interp: &mut Artichoke, mut regexp: Value) -> Result<Value, Error> {
134    let regexp = unsafe { Regexp::unbox_from_value(&mut regexp, interp)? };
135    let hash = regexp.hash();
136    // bit cast to an `i64` to ensure the value is signed. We're computing a
137    // hash so the sign of the value is not important.
138    let hash = i64::from_ne_bytes(hash.to_ne_bytes());
139    Ok(interp.convert(hash))
140}
141
142pub fn inspect(interp: &mut Artichoke, mut regexp: Value) -> Result<Value, Error> {
143    let regexp = unsafe { Regexp::unbox_from_value(&mut regexp, interp)? };
144    let inspect = regexp.inspect();
145    interp.try_convert_mut(inspect)
146}
147
148pub fn named_captures(interp: &mut Artichoke, mut regexp: Value) -> Result<Value, Error> {
149    let regexp = unsafe { Regexp::unbox_from_value(&mut regexp, interp)? };
150    let named_captures = regexp.named_captures()?;
151    interp.try_convert_mut(named_captures)
152}
153
154pub fn names(interp: &mut Artichoke, mut regexp: Value) -> Result<Value, Error> {
155    let regexp = unsafe { Regexp::unbox_from_value(&mut regexp, interp)? };
156    let names = regexp.names();
157    interp.try_convert_mut(names)
158}
159
160pub fn options(interp: &mut Artichoke, mut regexp: Value) -> Result<Value, Error> {
161    let regexp = unsafe { Regexp::unbox_from_value(&mut regexp, interp)? };
162    let opts = regexp.options();
163    Ok(interp.convert(opts))
164}
165
166pub fn source(interp: &mut Artichoke, mut regexp: Value) -> Result<Value, Error> {
167    let regexp = unsafe { Regexp::unbox_from_value(&mut regexp, interp)? };
168    let source = regexp.source();
169    interp.try_convert_mut(source)
170}
171
172pub fn to_s(interp: &mut Artichoke, mut regexp: Value) -> Result<Value, Error> {
173    let regexp = unsafe { Regexp::unbox_from_value(&mut regexp, interp)? };
174    let s = regexp.string();
175    interp.try_convert_mut(s)
176}
177
178#[cfg(test)]
179mod tests {
180    use super::*;
181    use crate::test::prelude::*;
182
183    #[test]
184    fn should_raise_frozen_error() {
185        let mut interp = interpreter();
186        let pattern = interp.try_convert_mut("xyz").unwrap();
187        let options = None;
188        let encoding = None;
189        let slf = interp.eval(b"/abc/").unwrap();
190        let result = initialize(&mut interp, pattern, options, encoding, slf);
191        assert_eq!(
192            "FrozenError (can't modify literal regexp)",
193            result.unwrap_err().to_string()
194        );
195    }
196}