artichoke_backend/convert/
fixnum.rs1use crate::Artichoke;
2use crate::convert::{BoxIntoRubyError, UnboxRubyError};
3use crate::core::{Convert, TryConvert, Value as _};
4use crate::error::Error;
5use crate::sys;
6use crate::types::{Ruby, Rust};
7use crate::value::Value;
8
9impl Convert<u8, Value> for Artichoke {
10 #[inline]
11 fn convert(&self, value: u8) -> Value {
12 self.convert(i64::from(value))
13 }
14}
15
16impl Convert<u16, Value> for Artichoke {
17 #[inline]
18 fn convert(&self, value: u16) -> Value {
19 self.convert(i64::from(value))
20 }
21}
22
23impl Convert<u32, Value> for Artichoke {
24 #[inline]
25 fn convert(&self, value: u32) -> Value {
26 self.convert(i64::from(value))
27 }
28}
29
30impl TryConvert<u64, Value> for Artichoke {
31 type Error = Error;
32
33 fn try_convert(&self, value: u64) -> Result<Value, Self::Error> {
34 let Ok(value) = i64::try_from(value) else {
35 return Err(BoxIntoRubyError::new(Rust::UnsignedInt, Ruby::Fixnum).into());
36 };
37 let fixnum = unsafe { sys::mrb_sys_fixnum_value(value) };
41 Ok(Value::from(fixnum))
42 }
43}
44
45impl TryConvert<usize, Value> for Artichoke {
46 type Error = Error;
47
48 fn try_convert(&self, value: usize) -> Result<Value, Self::Error> {
49 let Ok(value) = i64::try_from(value) else {
50 return Err(BoxIntoRubyError::new(Rust::UnsignedInt, Ruby::Fixnum).into());
51 };
52 let fixnum = unsafe { sys::mrb_sys_fixnum_value(value) };
56 Ok(Value::from(fixnum))
57 }
58}
59
60impl Convert<i8, Value> for Artichoke {
61 #[inline]
62 fn convert(&self, value: i8) -> Value {
63 self.convert(i64::from(value))
64 }
65}
66
67impl Convert<i16, Value> for Artichoke {
68 #[inline]
69 fn convert(&self, value: i16) -> Value {
70 self.convert(i64::from(value))
71 }
72}
73
74impl Convert<i32, Value> for Artichoke {
75 #[inline]
76 fn convert(&self, value: i32) -> Value {
77 self.convert(i64::from(value))
78 }
79}
80
81impl TryConvert<isize, Value> for Artichoke {
82 type Error = Error;
83
84 fn try_convert(&self, value: isize) -> Result<Value, Self::Error> {
85 let Ok(value) = i64::try_from(value) else {
86 return Err(BoxIntoRubyError::new(Rust::SignedInt, Ruby::Fixnum).into());
87 };
88 let fixnum = unsafe { sys::mrb_sys_fixnum_value(value) };
92 Ok(Value::from(fixnum))
93 }
94}
95
96impl Convert<i64, Value> for Artichoke {
97 #[inline]
98 fn convert(&self, value: i64) -> Value {
99 let fixnum = unsafe { sys::mrb_sys_fixnum_value(value) };
103 Value::from(fixnum)
104 }
105}
106
107impl TryConvert<Value, i64> for Artichoke {
108 type Error = Error;
109
110 fn try_convert(&self, value: Value) -> Result<i64, Self::Error> {
111 let Ruby::Fixnum = value.ruby_type() else {
112 return Err(UnboxRubyError::new(&value, Rust::SignedInt).into());
113 };
114 let inner = value.inner();
115 Ok(unsafe { sys::mrb_sys_fixnum_to_cint(inner) })
117 }
118}
119
120impl TryConvert<Value, u32> for Artichoke {
121 type Error = Error;
122
123 fn try_convert(&self, value: Value) -> Result<u32, Self::Error> {
124 let Ruby::Fixnum = value.ruby_type() else {
125 return Err(UnboxRubyError::new(&value, Rust::SignedInt).into());
126 };
127 let inner = value.inner();
128 let num = unsafe { sys::mrb_sys_fixnum_to_cint(inner) };
129 let num = u32::try_from(num).map_err(|_| UnboxRubyError::new(&value, Rust::UnsignedInt))?;
130 Ok(num)
131 }
132}
133
134impl TryConvert<Value, usize> for Artichoke {
135 type Error = Error;
136
137 fn try_convert(&self, value: Value) -> Result<usize, Self::Error> {
138 let Ruby::Fixnum = value.ruby_type() else {
139 return Err(UnboxRubyError::new(&value, Rust::SignedInt).into());
140 };
141 let inner = value.inner();
142 let num = unsafe { sys::mrb_sys_fixnum_to_cint(inner) };
143 let num = usize::try_from(num).map_err(|_| UnboxRubyError::new(&value, Rust::UnsignedInt))?;
144 Ok(num)
145 }
146}
147
148#[cfg(test)]
149mod tests {
150 use crate::test::prelude::*;
151
152 #[test]
153 fn fail_convert() {
154 let mut interp = interpreter();
155 let value = interp.eval(b"Object.new").unwrap();
157 let result = value.try_convert_into::<i64>(&interp);
158 assert!(result.is_err());
159 }
160
161 #[test]
162 fn prop_convert_to_fixnum() {
163 let interp = interpreter();
164 run_arbitrary::<i64>(|i| {
165 let value = interp.convert(i);
166 assert_eq!(value.ruby_type(), Ruby::Fixnum);
167 });
168 }
169
170 #[test]
171 fn prop_fixnum_with_value() {
172 let interp = interpreter();
173 run_arbitrary::<i64>(|i| {
174 let value = interp.convert(i);
175 let inner = value.inner();
176 let cint = unsafe { sys::mrb_sys_fixnum_to_cint(inner) };
177 assert_eq!(cint, i);
178 });
179 }
180
181 #[test]
182 fn prop_roundtrip() {
183 let interp = interpreter();
184 run_arbitrary::<i64>(|i| {
185 let value = interp.convert(i);
186 let value = value.try_convert_into::<i64>(&interp).unwrap();
187 assert_eq!(value, i);
188 });
189 }
190
191 #[test]
192 fn prop_roundtrip_err() {
193 let interp = interpreter();
194 for b in [true, false] {
195 let value = interp.convert(b);
196 let value = value.try_convert_into::<i64>(&interp);
197 assert!(value.is_err());
198 }
199 }
200
201 #[test]
202 fn test_fixnum_to_usize() {
203 let interp = interpreter();
204 let value = Convert::<_, Value>::convert(&*interp, 100);
205 let value = value.try_convert_into::<usize>(&interp).unwrap();
206 assert_eq!(100, value);
207 let value = Convert::<_, Value>::convert(&*interp, -100);
208 let value = value.try_convert_into::<usize>(&interp);
209 assert!(value.is_err());
210 }
211}