rutie/class/
hash.rs

1use std::{convert::From, default::Default};
2
3use crate::{
4    binding::hash,
5    types::{Value, ValueType},
6    AnyObject, Object, VerifiedObject,
7};
8
9/// `Hash`
10#[derive(Debug)]
11#[repr(C)]
12pub struct Hash {
13    value: Value,
14}
15
16impl Hash {
17    /// Creates a new instance of empty `Hash`.
18    ///
19    /// # Examples
20    ///
21    /// ```
22    /// use rutie::{Hash, VM};
23    /// # VM::init();
24    ///
25    /// Hash::new();
26    /// ```
27    ///
28    /// Ruby:
29    ///
30    /// ```ruby
31    /// {}
32    /// ```
33    pub fn new() -> Self {
34        Self::from(hash::new())
35    }
36
37    /// Retrieves an `AnyObject` from element stored at `key` key.
38    ///
39    /// # Examples
40    ///
41    /// ```
42    /// use rutie::{Fixnum, Hash, Object, Symbol, VM};
43    /// # VM::init();
44    ///
45    /// let mut hash = Hash::new();
46    ///
47    /// hash.store(Symbol::new("key"), Fixnum::new(1));
48    ///
49    /// assert_eq!(hash.at(&Symbol::new("key")).try_convert_to::<Fixnum>(), Ok(Fixnum::new(1)));
50    /// ```
51    ///
52    /// Ruby:
53    ///
54    /// ```ruby
55    /// hash = {}
56    /// hash[:key] = 1
57    ///
58    /// hash[:key] == 1
59    /// ```
60    pub fn at<T: Object>(&self, key: &T) -> AnyObject {
61        let result = hash::aref(self.value(), key.value());
62
63        AnyObject::from(result)
64    }
65
66    /// Associates the `value` with the `key`.
67    ///
68    /// Both `key` and `value` must be types which implement `Object` trait.
69    ///
70    /// # Examples
71    ///
72    /// ```
73    /// use rutie::{Fixnum, Hash, Object, Symbol, VM};
74    /// # VM::init();
75    ///
76    /// let mut hash = Hash::new();
77    ///
78    /// hash.store(Symbol::new("key"), Fixnum::new(1));
79    ///
80    /// assert_eq!(hash.at(&Symbol::new("key")).try_convert_to::<Fixnum>(), Ok(Fixnum::new(1)));
81    /// ```
82    ///
83    /// Ruby:
84    ///
85    /// ```ruby
86    /// hash = {}
87    /// hash[:key] = 1
88    ///
89    /// hash[:key] == 1
90    /// ```
91    pub fn store<K: Object, V: Object>(&mut self, key: K, value: V) -> AnyObject {
92        let result = hash::aset(self.value(), key.value(), value.value());
93
94        AnyObject::from(result)
95    }
96
97    /// Retrieves the length of the hash.
98    ///
99    /// # Examples
100    ///
101    /// ```
102    /// use rutie::{Hash, Fixnum, Symbol, VM};
103    /// # VM::init();
104    ///
105    /// let mut hash = Hash::new();
106    ///
107    /// hash.store(Symbol::new("key1"), Fixnum::new(1));
108    /// assert_eq!(hash.length(), 1);
109    ///
110    /// hash.store(Symbol::new("key2"), Fixnum::new(2));
111    /// assert_eq!(hash.length(), 2);
112    /// ```
113    ///
114    /// Ruby:
115    ///
116    /// ```ruby
117    /// hash = {}
118    ///
119    /// hash[:key1] = 1
120    /// hash.length == 1
121    ///
122    /// hash[:key2] = 2
123    /// hash.length == 2
124    /// ```
125    pub fn length(&self) -> usize {
126        hash::length(self.value()) as usize
127    }
128
129    /// Removes all key-value pairs.
130    ///
131    /// # Examples
132    ///
133    /// ```
134    /// use rutie::{Hash, Fixnum, Symbol, VM};
135    /// # VM::init();
136    ///
137    /// let mut hash = Hash::new();
138    ///
139    /// hash.store(Symbol::new("key1"), Fixnum::new(1));
140    /// hash.store(Symbol::new("key2"), Fixnum::new(2));
141    /// assert_eq!(hash.length(), 2);
142    ///
143    /// hash.clear();
144    /// assert_eq!(hash.length(), 0);
145    /// ```
146    ///
147    /// Ruby:
148    ///
149    /// ```ruby
150    /// hash = {}
151    ///
152    /// hash[:key1] = 1
153    /// hash[:key2] = 2
154    /// hash.length == 2
155    ///
156    /// hash.clear
157    ///
158    /// hash.length == 0
159    /// ```
160    pub fn clear(&self) {
161        hash::clear(self.value())
162    }
163
164    /// Deletes the key-value pair and returns the value from hash whose key is equal to key. If
165    /// the key is not found, it returns nil.
166    ///
167    /// `key` must be a type which implements the `Object` trait.
168    ///
169    /// # Examples
170    ///
171    /// ```
172    /// use rutie::{Fixnum, Hash, Object, Symbol, VM};
173    /// # VM::init();
174    ///
175    /// let mut hash = Hash::new();
176    ///
177    /// hash.store(Symbol::new("key1"), Fixnum::new(1));
178    /// hash.store(Symbol::new("key2"), Fixnum::new(2));
179    /// assert_eq!(hash.length(), 2);
180    ///
181    /// let deleted = hash.delete(Symbol::new("key2"));
182    /// assert_eq!(hash.length(), 1);
183    /// assert_eq!(deleted.try_convert_to::<Fixnum>(), Ok(Fixnum::new(2)));
184    /// ```
185    ///
186    /// Ruby:
187    ///
188    /// ```ruby
189    /// hash = {}
190    ///
191    /// hash[:key1] = 1
192    /// hash[:key2] = 2
193    /// hash.length == 2
194    ///
195    /// deleted = hash.delete(:key2)
196    ///
197    /// hash.length == 1
198    /// deleted == 2
199    /// ```
200    pub fn delete<K: Object>(&mut self, key: K) -> AnyObject {
201        let result = hash::delete(self.value(), key.value());
202
203        AnyObject::from(result)
204    }
205
206    /// Runs a closure for each `key` and `value` pair.
207    ///
208    /// Key and value have `AnyObject` type.
209    ///
210    /// # Examples
211    ///
212    /// ```
213    /// use rutie::{Fixnum, Hash, Object, Symbol, VM};
214    /// # VM::init();
215    ///
216    /// let mut hash = Hash::new();
217    ///
218    /// hash.store(Symbol::new("first_key"), Fixnum::new(1));
219    /// hash.store(Symbol::new("second_key"), Fixnum::new(2));
220    ///
221    /// let mut doubled_values: Vec<i64> = Vec::new();
222    ///
223    /// hash.each(|_key, value| {
224    ///     if let Ok(value) = value.try_convert_to::<Fixnum>() {
225    ///         doubled_values.push(value.to_i64() * 2);
226    ///     }
227    /// });
228    ///
229    /// assert_eq!(doubled_values, vec![2, 4]);
230    /// ```
231    ///
232    /// Ruby:
233    ///
234    /// ```ruby
235    /// hash = {
236    ///   first_key: 1,
237    ///   second_key: 2
238    /// }
239    ///
240    /// doubled_values = []
241    ///
242    /// hash.each do |_key, value|
243    ///   doubled_values << [value * 2]
244    /// end
245    ///
246    /// doubled_values == [2, 4]
247    /// ```
248    pub fn each<F>(&self, closure: F)
249    where
250        F: FnMut(AnyObject, AnyObject),
251    {
252        hash::each(self.value(), closure);
253    }
254}
255
256impl Clone for Hash {
257    fn clone(&self) -> Hash {
258        Hash {
259            value: hash::dup(self.value()),
260        }
261    }
262}
263
264impl Default for Hash {
265    fn default() -> Self {
266        Hash::new()
267    }
268}
269
270impl From<Value> for Hash {
271    fn from(value: Value) -> Self {
272        Hash { value }
273    }
274}
275
276impl Into<Value> for Hash {
277    fn into(self) -> Value {
278        self.value
279    }
280}
281
282impl Into<AnyObject> for Hash {
283    fn into(self) -> AnyObject {
284        AnyObject::from(self.value)
285    }
286}
287
288impl Object for Hash {
289    #[inline]
290    fn value(&self) -> Value {
291        self.value
292    }
293}
294
295impl VerifiedObject for Hash {
296    fn is_correct_type<T: Object>(object: &T) -> bool {
297        object.value().ty() == ValueType::Hash
298    }
299
300    fn error_message() -> &'static str {
301        "Error converting to Hash"
302    }
303}
304
305impl PartialEq for Hash {
306    fn eq(&self, other: &Self) -> bool {
307        self.equals(other)
308    }
309}
310
311#[cfg(test)]
312mod tests {
313    use super::super::super::{Fixnum, Hash, Object, Symbol, LOCK_FOR_TEST, VM};
314
315    #[test]
316    fn test_hash_each() {
317        let _guard = LOCK_FOR_TEST.write().unwrap();
318        VM::init();
319
320        let mut hash = Hash::new();
321
322        let len: i64 = 200;
323
324        for i in 0..len {
325            hash.store(Symbol::new(&format!("key_{}", i)), Fixnum::new(i));
326        }
327
328        assert_eq!(hash.length(), len as usize);
329
330        let mut counter: i64 = 0;
331
332        hash.each(|k, v| {
333            assert_eq!(
334                k.try_convert_to::<Symbol>().map(|s| s.to_string()),
335                Ok(format!("key_{}", counter))
336            );
337            assert_eq!(
338                v.try_convert_to::<Fixnum>().map(|f| f.to_i64()),
339                Ok(counter)
340            );
341
342            counter += 1;
343        });
344
345        assert_eq!(counter, len);
346    }
347}