1pub use crate::core::{Ruby, Rust};
2use crate::sys;
3
4#[must_use]
9pub fn ruby_from_mrb_value(value: sys::mrb_value) -> Ruby {
10 use sys::mrb_vtype::{
11 MRB_TT_ARRAY, MRB_TT_BIGINT, MRB_TT_BREAK, MRB_TT_CDATA, MRB_TT_CLASS, MRB_TT_COMPLEX, MRB_TT_CPTR,
12 MRB_TT_ENV, MRB_TT_EXCEPTION, MRB_TT_FALSE, MRB_TT_FIBER, MRB_TT_FLOAT, MRB_TT_FREE, MRB_TT_HASH,
13 MRB_TT_ICLASS, MRB_TT_INTEGER, MRB_TT_ISTRUCT, MRB_TT_MAXDEFINE, MRB_TT_MODULE, MRB_TT_OBJECT, MRB_TT_PROC,
14 MRB_TT_RANGE, MRB_TT_RATIONAL, MRB_TT_SCLASS, MRB_TT_STRING, MRB_TT_STRUCT, MRB_TT_SYMBOL, MRB_TT_TRUE,
15 MRB_TT_UNDEF,
16 };
17
18 #[expect(clippy::match_same_arms, reason = "match arms are ordered by `sys::mrb_vtype` enum")]
19 match value.tt {
20 MRB_TT_FALSE if sys::mrb_sys_value_is_nil(value) => Ruby::Nil,
24 MRB_TT_FALSE => Ruby::Bool,
25 MRB_TT_FREE => Ruby::Unreachable,
29 MRB_TT_TRUE => Ruby::Bool,
30 MRB_TT_INTEGER => Ruby::Fixnum,
31 MRB_TT_SYMBOL => Ruby::Symbol,
32 MRB_TT_UNDEF => Ruby::Unreachable,
34 MRB_TT_FLOAT => Ruby::Float,
35 MRB_TT_CPTR => Ruby::CPointer,
37 MRB_TT_OBJECT => Ruby::Object,
38 MRB_TT_CLASS => Ruby::Class,
39 MRB_TT_MODULE => Ruby::Module,
40 MRB_TT_ICLASS => Ruby::Unreachable,
43 MRB_TT_SCLASS => Ruby::SingletonClass,
62 MRB_TT_PROC => Ruby::Proc,
63 MRB_TT_ARRAY => Ruby::Array,
67 MRB_TT_HASH => Ruby::Hash,
68 MRB_TT_STRING => Ruby::String,
69 MRB_TT_RANGE => Ruby::Range,
70 MRB_TT_EXCEPTION => Ruby::Exception,
71 MRB_TT_ENV => Ruby::Unreachable,
74 MRB_TT_CDATA => Ruby::Data,
78 MRB_TT_FIBER => Ruby::Fiber,
80 MRB_TT_STRUCT => Ruby::Unreachable,
81 MRB_TT_ISTRUCT => Ruby::InlineStruct,
88 MRB_TT_BREAK => Ruby::Unreachable,
95 MRB_TT_COMPLEX => Ruby::Unreachable,
96 MRB_TT_RATIONAL => Ruby::Unreachable,
97 MRB_TT_BIGINT => Ruby::Unreachable,
98 MRB_TT_MAXDEFINE => Ruby::Unreachable,
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use std::collections::HashMap;
108
109 use crate::test::prelude::*;
110 use crate::types;
111
112 #[test]
113 fn parse_nil_ruby_type() {
114 let nil = Value::nil();
115 assert_eq!(Ruby::Nil, types::ruby_from_mrb_value(nil.inner()));
116 }
117
118 #[test]
119 fn parse_bool_ruby_type() {
120 let interp = interpreter();
121 let yes = interp.convert(true);
122 assert_eq!(Ruby::Bool, types::ruby_from_mrb_value(yes.inner()));
123 let no = interp.convert(false);
124 assert_eq!(Ruby::Bool, types::ruby_from_mrb_value(no.inner()));
125 }
126
127 #[test]
128 fn parse_fixnum_ruby_type() {
129 let interp = interpreter();
130 let zero = interp.convert(0_i64);
131 assert_eq!(Ruby::Fixnum, types::ruby_from_mrb_value(zero.inner()));
132 let thousand = interp.convert(1000_i64);
133 assert_eq!(Ruby::Fixnum, types::ruby_from_mrb_value(thousand.inner()));
134 }
135
136 #[test]
137 fn parse_symbol_ruby_type() {
138 let mut interp = interpreter();
139 let empty = interp.eval(b":''").unwrap();
140 assert_eq!(Ruby::Symbol, types::ruby_from_mrb_value(empty.inner()));
141 let utf8 = interp.eval(b":Artichoke").unwrap();
142 assert_eq!(Ruby::Symbol, types::ruby_from_mrb_value(utf8.inner()));
143 let binary = interp.eval(br#":"\xFE""#).unwrap();
144 assert_eq!(Ruby::Symbol, types::ruby_from_mrb_value(binary.inner()));
145 }
146
147 #[test]
148 fn parse_float_ruby_type() {
149 let mut interp = interpreter();
150 let zero = interp.convert_mut(0.0_f64);
151 assert_eq!(Ruby::Float, types::ruby_from_mrb_value(zero.inner()));
152 let float = interp.convert_mut(1.5_f64);
153 assert_eq!(Ruby::Float, types::ruby_from_mrb_value(float.inner()));
154 }
155
156 #[test]
157 fn parse_object_ruby_type() {
158 let mut interp = interpreter();
159 let object = interp.eval(b"Object.new").unwrap();
160 assert_eq!(Ruby::Object, types::ruby_from_mrb_value(object.inner()));
161 #[cfg(feature = "core-env")]
162 {
163 let env = interp.eval(b"ENV").unwrap();
164 assert_eq!(Ruby::Object, types::ruby_from_mrb_value(env.inner()));
165 }
166 }
167
168 #[test]
169 fn parse_class_ruby_type() {
170 let mut interp = interpreter();
171 let builtin = interp.eval(b"Object").unwrap();
172 assert_eq!(Ruby::Class, types::ruby_from_mrb_value(builtin.inner()));
173 let data = interp.eval(b"Array").unwrap();
174 assert_eq!(Ruby::Class, types::ruby_from_mrb_value(data.inner()));
175 #[cfg(feature = "core-math")]
176 {
177 let source = interp.eval(b"Math::DomainError").unwrap();
178 assert_eq!(Ruby::Class, types::ruby_from_mrb_value(source.inner()));
179 }
180 }
181
182 #[test]
183 fn parse_module_ruby_type() {
184 let mut interp = interpreter();
185 let builtin = interp.eval(b"Comparable").unwrap();
186 assert_eq!(Ruby::Module, types::ruby_from_mrb_value(builtin.inner()));
187 #[cfg(feature = "core-math")]
188 {
189 let data = interp.eval(b"Math").unwrap();
190 assert_eq!(Ruby::Module, types::ruby_from_mrb_value(data.inner()));
191 }
192 let artichoke = interp.eval(b"Artichoke").unwrap();
193 assert_eq!(Ruby::Module, types::ruby_from_mrb_value(artichoke.inner()));
194 }
195
196 #[test]
197 fn parse_proc_ruby_type() {
198 let mut interp = interpreter();
199 let literal = interp.eval(b"proc {}").unwrap();
200 assert_eq!(Ruby::Proc, types::ruby_from_mrb_value(literal.inner()));
201 let proc = interp.eval(b"Proc.new {}").unwrap();
202 assert_eq!(Ruby::Proc, types::ruby_from_mrb_value(proc.inner()));
203 let lambda = interp.eval(b"lambda {}").unwrap();
204 assert_eq!(Ruby::Proc, types::ruby_from_mrb_value(lambda.inner()));
205 let stabby = interp.eval(b"->() {}").unwrap();
206 assert_eq!(Ruby::Proc, types::ruby_from_mrb_value(stabby.inner()));
207 }
208
209 #[test]
210 fn parse_string_ruby_type() {
211 let mut interp = interpreter();
212 let empty = interp.try_convert_mut("").unwrap();
213 assert_eq!(Ruby::String, types::ruby_from_mrb_value(empty.inner()));
214 let utf8 = interp.try_convert_mut("Artichoke").unwrap();
215 assert_eq!(Ruby::String, types::ruby_from_mrb_value(utf8.inner()));
216 let binary = interp.try_convert_mut(vec![0xFF_u8, 0x00, 0xFE]).unwrap();
217 assert_eq!(Ruby::String, types::ruby_from_mrb_value(binary.inner()));
218 }
219
220 #[test]
221 fn parse_array_ruby_type() {
222 let mut interp = interpreter();
223 let empty = interp.eval(b"[]").unwrap();
224 assert_eq!(Ruby::Array, types::ruby_from_mrb_value(empty.inner()));
225 #[cfg(feature = "core-regexp")]
226 {
227 let array = interp.eval(b"[1, /./, Object.new]").unwrap();
228 assert_eq!(Ruby::Array, types::ruby_from_mrb_value(array.inner()));
229 }
230 let ary = vec!["a", "b", "c"];
231 let converted = interp.try_convert_mut(ary).unwrap();
232 assert_eq!(Ruby::Array, types::ruby_from_mrb_value(converted.inner()));
233 }
234
235 #[test]
236 fn parse_hash_ruby_type() {
237 let mut interp = interpreter();
238 let empty = interp.eval(b"{}").unwrap();
239 assert_eq!(Ruby::Hash, types::ruby_from_mrb_value(empty.inner()));
240 #[cfg(feature = "core-regexp")]
241 {
242 let hash = interp.eval(b"{a: 1, b: [/./]}").unwrap();
243 assert_eq!(Ruby::Hash, types::ruby_from_mrb_value(hash.inner()));
244 }
245 let mut map = HashMap::default();
246 map.insert(b"a".to_vec(), vec![0_u8]);
247 map.insert(b"b".to_vec(), b"binary".to_vec());
248 let converted = interp.try_convert_mut(map).unwrap();
249 assert_eq!(Ruby::Hash, types::ruby_from_mrb_value(converted.inner()));
250 }
251
252 #[test]
253 fn parse_range_ruby_type() {
254 let mut interp = interpreter();
255 let dot2 = interp.eval(b"0..0").unwrap();
256 assert_eq!(Ruby::Range, types::ruby_from_mrb_value(dot2.inner()));
257 let dot3 = interp.eval(b"0...0").unwrap();
258 assert_eq!(Ruby::Range, types::ruby_from_mrb_value(dot3.inner()));
259 }
260
261 #[test]
262 fn parse_exception_ruby_type() {
263 let mut interp = interpreter();
264 let root = interp.eval(b"Exception.new").unwrap();
265 assert_eq!(Ruby::Exception, types::ruby_from_mrb_value(root.inner()));
266 let stderror = interp.eval(b"StandardError.new").unwrap();
267 assert_eq!(Ruby::Exception, types::ruby_from_mrb_value(stderror.inner()));
268 let index = interp.eval(b"IndexError.new").unwrap();
269 assert_eq!(Ruby::Exception, types::ruby_from_mrb_value(index.inner()));
270 #[cfg(feature = "core-math")]
271 {
272 let domain = interp.eval(b"Math::DomainError.new").unwrap();
273 assert_eq!(Ruby::Exception, types::ruby_from_mrb_value(domain.inner()));
274 }
275 }
276}