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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
use std::mem;
use crate::convert::RustBackedValue;
use crate::extn::core::regexp::enc::{self, Encoding};
use crate::extn::core::regexp::opts::{self, Options};
use crate::extn::core::regexp::Regexp;
use crate::sys;
use crate::value::Value;
use crate::warn::MrbWarn;
use crate::Mrb;
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub enum Error {
Fatal,
NoImplicitConversionToString,
Syntax,
Unicode,
}
#[derive(Debug)]
pub struct Args {
pub pattern: Value,
pub options: Option<Options>,
pub encoding: Option<Encoding>,
}
impl Args {
const ARGSPEC: &'static [u8] = b"o|o?o?\0";
pub unsafe fn extract(interp: &Mrb) -> Result<Self, Error> {
let mut pattern = <mem::MaybeUninit<sys::mrb_value>>::uninit();
let mut opts = <mem::MaybeUninit<sys::mrb_value>>::uninit();
let mut has_opts = <mem::MaybeUninit<sys::mrb_bool>>::uninit();
let mut enc = <mem::MaybeUninit<sys::mrb_value>>::uninit();
let mut has_enc = <mem::MaybeUninit<sys::mrb_bool>>::uninit();
sys::mrb_get_args(
interp.borrow().mrb,
Self::ARGSPEC.as_ptr() as *const i8,
pattern.as_mut_ptr(),
opts.as_mut_ptr(),
has_opts.as_mut_ptr(),
enc.as_mut_ptr(),
has_enc.as_mut_ptr(),
);
let pattern = pattern.assume_init();
let has_opts = has_opts.assume_init() != 0;
let has_enc = has_enc.assume_init() != 0;
let pattern = Value::new(&interp, pattern);
let options = if has_opts {
Some(opts::parse(&Value::new(interp, opts.assume_init())))
} else {
None
};
let encoding = if has_enc {
let encoding = Value::new(interp, enc.assume_init());
match enc::parse(&encoding) {
Ok(encoding) => Some(encoding),
Err(enc::Error::InvalidEncoding) => {
let warning = format!("encoding option is ignored -- {}", encoding.to_s());
interp.warn(warning.as_str()).map_err(|_| Error::Fatal)?;
None
}
}
} else if has_opts {
let encoding = Value::new(interp, opts.assume_init());
match enc::parse(&encoding) {
Ok(encoding) => Some(encoding),
Err(enc::Error::InvalidEncoding) => {
let warning = format!("encoding option is ignored -- {}", encoding.to_s());
interp.warn(warning.as_str()).map_err(|_| Error::Fatal)?;
None
}
}
} else {
None
};
Ok(Self {
pattern,
options,
encoding,
})
}
}
pub fn method(interp: &Mrb, args: Args, slf: sys::mrb_value) -> Result<Value, Error> {
let mut literal_options = args.options.unwrap_or_default();
let literal_pattern =
if let Ok(regexp) = unsafe { Regexp::try_from_ruby(interp, &args.pattern) } {
interp
.warn("flags ignored when initializing from Regexp")
.map_err(|_| Error::Fatal)?;
let borrow = regexp.borrow();
literal_options = borrow.options;
borrow.literal_pattern.clone()
} else {
let bytes = args
.pattern
.try_into::<Vec<u8>>()
.map_err(|_| Error::NoImplicitConversionToString)?;
String::from_utf8(bytes).map_err(|_| Error::Unicode)?
};
let (pattern, options) = opts::parse_pattern(literal_pattern.as_str(), literal_options);
if let Some(data) = Regexp::new(
literal_pattern,
pattern,
literal_options,
options,
args.encoding.unwrap_or_default(),
) {
unsafe {
data.try_into_ruby(interp, Some(slf))
.map_err(|_| Error::Fatal)
}
} else {
Err(Error::Syntax)
}
}