1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
use std::convert::TryFrom;
use std::mem;
use crate::convert::{FromMrb, RustBackedValue, TryFromMrb};
use crate::extn::core::regexp::Regexp;
use crate::sys;
use crate::value::Value;
use crate::Mrb;
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub enum Error {
Fatal,
PosType,
StringType,
}
#[derive(Debug)]
pub struct Args {
pub string: Option<String>,
pub pos: Option<i64>,
}
impl Args {
const ARGSPEC: &'static [u8] = b"o|o?\0";
pub unsafe fn extract(interp: &Mrb) -> Result<Self, Error> {
let mut string = <mem::MaybeUninit<sys::mrb_value>>::uninit();
let mut pos = <mem::MaybeUninit<sys::mrb_value>>::uninit();
let mut has_pos = <mem::MaybeUninit<sys::mrb_bool>>::uninit();
sys::mrb_get_args(
interp.borrow().mrb,
Self::ARGSPEC.as_ptr() as *const i8,
string.as_mut_ptr(),
pos.as_mut_ptr(),
has_pos.as_mut_ptr(),
);
let string = string.assume_init();
let has_pos = has_pos.assume_init() != 0;
let string = if let Ok(string) =
<Option<String>>::try_from_mrb(&interp, Value::new(interp, string))
{
string
} else {
return Err(Error::StringType);
};
let pos = if has_pos {
let pos = i64::try_from_mrb(&interp, Value::new(&interp, pos.assume_init()))
.map_err(|_| Error::PosType)?;
Some(pos)
} else {
None
};
Ok(Self { string, pos })
}
}
pub fn method(interp: &Mrb, args: Args, value: &Value) -> Result<Value, Error> {
let data = unsafe { Regexp::try_from_ruby(interp, value) }.map_err(|_| Error::Fatal)?;
let string = if let Some(string) = args.string {
string
} else {
return Ok(Value::from_mrb(interp, false));
};
let pos = args.pos.unwrap_or_default();
let pos = if pos < 0 {
let strlen = i64::try_from(string.chars().count()).unwrap_or_default();
let pos = strlen + pos;
if pos < 0 {
return Ok(Value::from_mrb(interp, false));
}
usize::try_from(pos).map_err(|_| Error::Fatal)?
} else {
usize::try_from(pos).map_err(|_| Error::Fatal)?
};
if pos > string.chars().count() {
return Ok(Value::from_mrb(interp, false));
}
let byte_offset = string.chars().take(pos).collect::<String>().len();
let borrow = data.borrow();
let regex = (*borrow.regex).as_ref().ok_or(Error::Fatal)?;
let match_target = &string[byte_offset..];
Ok(Value::from_mrb(interp, regex.find(match_target).is_some()))
}