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}