• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mattwparas / steel / 19642897388

24 Nov 2025 05:14PM UTC coverage: 49.283% (-0.03%) from 49.314%
19642897388

Pull #574

github

web-flow
Merge fde67cc60 into ed9e0c34e
Pull Request #574: Add bitwise operators and hash code function

7 of 51 new or added lines in 3 files covered. (13.73%)

66 existing lines in 5 files now uncovered.

15943 of 32350 relevant lines covered (49.28%)

3471369.54 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

50.0
/crates/steel-core/src/primitives/hashmaps.rs
1
use std::hash::{DefaultHasher, Hash, Hasher};
2

3
use crate::rvals::SteelHashMap;
4
use crate::stop;
5
use crate::values::HashMap;
6
use crate::{core::utils::declare_const_ref_functions, gc::Gc};
7
use crate::{
8
    rvals::{Result, SteelVal},
9
    steel_vm::builtin::BuiltInModule,
10
};
11

12
use crate::primitives::vectors::vec_construct_iter_normal;
13

14
use num_bigint::BigInt;
15
use steel_derive::function;
16

17
declare_const_ref_functions!(
18
    HM_CONSTRUCT => hm_construct,
19
    HM_GET => steel_hash_ref,
20
    // HM_EMPTY => hm_empty,
21
);
22

23
pub const HM_INSERT: SteelVal = SteelVal::MutFunc(steel_hash_insert);
24

25
pub(crate) fn hashmap_module() -> BuiltInModule {
4✔
26
    let mut module = BuiltInModule::new("steel/hash".to_string());
16✔
27
    module
68✔
28
        .register_native_fn_definition(HM_CONSTRUCT_DEFINITION)
64✔
29
        .register_value("%keyword-hash", SteelVal::FuncV(hm_construct_keywords))
64✔
30
        .register_native_fn_definition(HASH_INSERT_DEFINITION)
56✔
31
        .register_native_fn_definition(HASH_REF_DEFINITION)
52✔
32
        .register_value("hash-get", SteelVal::FuncV(steel_hash_ref))
52✔
33
        .register_native_fn_definition(HASH_TRY_GET_DEFINITION)
44✔
34
        .register_native_fn_definition(HASH_LENGTH_DEFINITION)
40✔
35
        .register_native_fn_definition(HASH_CONTAINS_DEFINITION)
36✔
36
        .register_native_fn_definition(KEYS_TO_LIST_DEFINITION)
32✔
37
        .register_native_fn_definition(KEYS_TO_VECTOR_DEFINITION)
28✔
38
        .register_native_fn_definition(VALUES_TO_LIST_DEFINITION)
24✔
39
        .register_native_fn_definition(VALUES_TO_VECTOR_DEFINITION)
20✔
40
        .register_native_fn_definition(CLEAR_DEFINITION)
16✔
41
        .register_native_fn_definition(HM_EMPTY_DEFINITION)
12✔
42
        .register_native_fn_definition(HM_UNION_DEFINITION)
8✔
43
        .register_native_fn_definition(HASH_REMOVE_DEFINITION)
4✔
44
        .register_native_fn_definition(HASH_CODE_DEFINITION);
8✔
45
    module
4✔
46
}
47

48
#[steel_derive::function(name = "hash-code", constant = false)]
NEW
49
pub fn hash_code(arg: &SteelVal) -> Result<SteelVal> {
×
NEW
50
    let mut hasher = DefaultHasher::new();
×
51

NEW
52
    if !arg.is_hashable() {
×
NEW
53
        stop!(TypeMismatch => "value is not hashable: {}", arg);
×
54
    }
55

NEW
56
    arg.hash(&mut hasher);
×
NEW
57
    let value = hasher.finish();
×
58

NEW
59
    if let Ok(v) = isize::try_from(value) {
×
NEW
60
        Ok(SteelVal::IntV(v))
×
61
    } else {
NEW
62
        Ok(SteelVal::BigNum(Gc::new(BigInt::from(value))))
×
63
    }
64
}
65

66
/// Creates an immutable hash table with each given `key` mapped to the following `val`.
67
/// Each key must have a val, so the total number of arguments must be even.
68
///
69
/// (hash key val ...) -> hash?
70
///
71
/// * key : hashable?
72
/// * val : any/c
73
///
74
/// Note: the keys must be hashable.
75
///
76
/// # Examples
77
/// ```scheme
78
/// > (hash 'a 10 'b 20)
79
/// => '#hash((a . 10) (b . 20))
80
/// ```
81
#[steel_derive::native(name = "hash", arity = "AtLeast(0)")]
82
pub fn hm_construct(args: &[SteelVal]) -> Result<SteelVal> {
119,552✔
83
    let mut hm = HashMap::new();
239,104✔
84

85
    let mut arg_iter = args.iter().cloned();
478,208✔
86

87
    loop {
88
        match (arg_iter.next(), arg_iter.next()) {
1,062,420✔
89
            (Some(key), Some(value)) => {
292,106✔
90
                if key.is_hashable() {
438,159✔
91
                    hm.insert(key, value);
438,159✔
92
                } else {
UNCOV
93
                    stop!(TypeMismatch => "hash key not hashable!: {}", key);
×
94
                }
95
            }
96
            (None, None) => break,
119,552✔
97
            _ => {
UNCOV
98
                stop!(ArityMismatch => "hash map must have a value for every key!");
×
99
            }
100
        }
101
    }
102

103
    Ok(SteelVal::HashMapV(Gc::new(hm).into()))
239,104✔
104
}
105

UNCOV
106
pub fn hm_construct_keywords(args: &[SteelVal]) -> Result<SteelVal> {
×
UNCOV
107
    let mut hm = HashMap::new();
×
108

109
    let mut arg_iter = args.iter().cloned();
×
110

111
    loop {
UNCOV
112
        match (arg_iter.next(), arg_iter.next()) {
×
UNCOV
113
            (Some(key), Some(value)) => {
×
UNCOV
114
                if key.is_hashable() {
×
UNCOV
115
                    hm.insert(key, value);
×
116
                } else {
117
                    stop!(TypeMismatch => "hash key not hashable!: {}", key);
×
118
                }
119
            }
120
            (None, None) => break,
×
121
            _ => {
UNCOV
122
                stop!(ArityMismatch => "Missing keyword argument!");
×
123
            }
124
        }
125
    }
126

UNCOV
127
    Ok(SteelVal::HashMapV(Gc::new(hm).into()))
×
128
}
129

130
/// Returns a new hashmap with the given key removed. Performs a functional
131
/// update, so the old hash map is still available with the original key value pair.
132
///
133
/// (hash-remove map key) -> hash?
134
///
135
/// * map : hash?
136
/// * key : any/c
137
///
138
/// # Examples
139
///
140
/// ```scheme
141
/// > (hash-remove (hash 'a 10 'b 20) 'a)
142
/// => '#hash(('b . 20))
143
/// ```
144
#[function(name = "hash-remove")]
UNCOV
145
pub fn hash_remove(map: &mut SteelVal, key: SteelVal) -> Result<SteelVal> {
×
UNCOV
146
    if key.is_hashable() {
×
UNCOV
147
        if let SteelVal::HashMapV(SteelHashMap(ref mut m)) = map {
×
UNCOV
148
            match Gc::get_mut(m) {
×
UNCOV
149
                Some(m) => {
×
UNCOV
150
                    m.remove(&key);
×
UNCOV
151
                    Ok(std::mem::replace(map, SteelVal::Void))
×
152
                }
153
                None => {
UNCOV
154
                    let mut m = m.unwrap();
×
UNCOV
155
                    m.remove(&key);
×
156

157
                    Ok(SteelVal::HashMapV(Gc::new(m).into()))
×
158
                }
159
            }
160
        } else {
161
            stop!(TypeMismatch => "hash-remove expects a hash map, found: {:?}", map);
×
162
        }
163
    } else {
UNCOV
164
        stop!(TypeMismatch => "hash key not hashable: {:?}", key)
×
165
    }
166
}
167

168
/// Returns a new hashmap with the additional key value pair added. Performs a functional update,
169
/// so the old hash map is still accessible.
170
///
171
/// (hash-insert map key val) -> hash?
172
///
173
/// * map : hash?
174
/// * key : any/c
175
/// * val : any/c
176
///
177
/// # Examples
178
///
179
/// ```scheme
180
/// > (hash-insert (hash 'a 10 'b 20) 'c 30)
181
/// => '#hash((a . 10) (b . 20) (c . 30))
182
/// ```
183
#[function(name = "hash-insert")]
184
pub fn hash_insert(
1,499✔
185
    map: &mut SteelVal,
186
    key: &mut SteelVal,
187
    value: &mut SteelVal,
188
) -> Result<SteelVal> {
189
    if key.is_hashable() {
2,998✔
190
        let key = std::mem::take(key);
4,497✔
191
        let value = std::mem::take(value);
4,497✔
192
        if let SteelVal::HashMapV(SteelHashMap(ref mut m)) = map {
2,998✔
193
            match Gc::get_mut(m) {
1,499✔
194
                Some(m) => {
1,444✔
195
                    m.insert(key, value);
5,776✔
196
                    Ok(std::mem::replace(map, SteelVal::Void))
2,888✔
197
                }
198
                None => Ok(SteelVal::HashMapV(Gc::new(m.update(key, value)).into())),
220✔
199
            }
200
        } else {
UNCOV
201
            stop!(TypeMismatch => "hash-insert expects a hash map, found: {:?}", map);
×
202
        }
203
    } else {
UNCOV
204
        stop!(TypeMismatch => "hash key not hashable: {:?}", key)
×
205
    }
206
}
207

208
/// Gets the `key` from the given `map`. Raises an error if the key does not exist. `hash-get` is an alias for this.
209
///
210
/// (hash-ref map key) -> any/c
211
///
212
/// * map : hash?
213
/// * key : any/c
214
///
215
/// # Examples
216
///
217
/// ```scheme
218
/// > (hash-ref (hash 'a 10 'b 20) 'b) ;; => 20
219
/// ```
220
#[function(name = "hash-ref")]
221
pub fn hash_ref(map: &Gc<HashMap<SteelVal, SteelVal>>, key: &SteelVal) -> Result<SteelVal> {
34,603✔
222
    if key.is_hashable() {
69,206✔
223
        match map.get(key) {
69,206✔
224
            Some(value) => Ok(value.clone()),
69,204✔
225
            None => stop!(Generic => "key not found in hash map: {} - map: {:?}", key, map),
2✔
226
        }
227
    } else {
UNCOV
228
        stop!(TypeMismatch => "key not hashable: {}", key)
×
229
    }
230
}
231

232
/// Gets the `key` from the given `map`. Returns #false if the key does not exist.
233
///
234
/// (hash-try-get map key) -> (or any/c #false)
235
///
236
/// * map : hash?
237
/// * key : any/c
238
///
239
/// # Examples
240
///
241
/// ```scheme
242
/// > (hash-try-get (hash 'a 10 'b 20) 'b) ;; => 20
243
/// > (hash-try-get (hash 'a 10 'b 20) 'does-not-exist) ;; => #false
244
/// ```
245
#[function(name = "hash-try-get")]
246
pub fn hash_try_get(map: &Gc<HashMap<SteelVal, SteelVal>>, key: &SteelVal) -> SteelVal {
4,415✔
247
    match map.get(key) {
8,830✔
248
        Some(v) => v.clone(),
1,893✔
249
        None => SteelVal::BoolV(false),
3,784✔
250
    }
251
}
252

253
/// Returns the number of key value pairs in the map
254
///
255
/// (hash-length map) -> (and/c positive? int?)
256
///
257
/// * map : hash?
258
///
259
/// # Examples
260
///
261
/// ```scheme
262
/// > (hash-length (hash 'a 10 'b 20)) ;; => 2
263
/// ```
264
#[function(name = "hash-length")]
UNCOV
265
pub fn hash_length(map: &Gc<HashMap<SteelVal, SteelVal>>) -> usize {
×
UNCOV
266
    map.len()
×
267
}
268

269
/// Checks whether the given map contains the given key. Key must be hashable.
270
///
271
/// (hash-contains? map key) -> bool?
272
///
273
/// * map : hash?
274
/// * key : hashable?
275
///
276
/// # Example
277
///
278
/// ```scheme
279
/// > (hash-contains? (hash 'a 10 'b 20) 'a) ;; => #true
280
/// > (hash-contains? (hash 'a 10 'b 20) 'not-there) ;; => #false
281
/// ```
282
#[function(name = "hash-contains?")]
283
pub fn hash_contains(map: &Gc<HashMap<SteelVal, SteelVal>>, key: &SteelVal) -> Result<SteelVal> {
3✔
284
    if key.is_hashable() {
6✔
285
        Ok(SteelVal::BoolV(map.contains_key(key)))
6✔
286
    } else {
UNCOV
287
        stop!(TypeMismatch => "hash key not hashable!: {}", key);
×
288
    }
289
}
290

291
/// Returns the keys of the given hash map as a list.
292
///
293
/// (hash-keys->list map) -> (listof hashable?)
294
///
295
/// * map : hash?
296
///
297
/// # Examples
298
///
299
/// ```scheme
300
/// > (hash-keys->list (hash 'a 10 'b 20))
301
/// => '(a b)
302
/// ```
303
#[function(name = "hash-keys->list")]
UNCOV
304
pub fn keys_to_list(hashmap: &Gc<HashMap<SteelVal, SteelVal>>) -> Result<SteelVal> {
×
UNCOV
305
    Ok(SteelVal::ListV(hashmap.keys().cloned().collect()))
×
306
}
307

308
/// Returns the values of the given hash map as a list
309
///
310
/// (hash-values->list map) -> (listof any/c)
311
///
312
/// * map : hash?
313
///
314
/// # Examples
315
///
316
/// ```scheme
317
/// > (hash-values->list (hash 'a 10 'b 20))
318
/// => '(10 20)
319
/// ```
320
#[steel_derive::function(name = "hash-values->list")]
UNCOV
321
pub fn values_to_list(hashmap: &Gc<HashMap<SteelVal, SteelVal>>) -> Result<SteelVal> {
×
UNCOV
322
    Ok(SteelVal::ListV(hashmap.values().cloned().collect()))
×
323
}
324

325
/// Returns the keys of the given hash map as an immutable vector
326
///
327
/// (hash-keys->vector map) -> (vectorof hashable?)
328
///
329
/// * map: hash?
330
///
331
/// # Examples
332
/// ```scheme
333
/// > (hash-keys->vector (hash 'a 10 'b 20))
334
/// => '#(a b)
335
/// ```
336
#[steel_derive::function(name = "hash-keys->vector")]
337
pub fn keys_to_vector(hashmap: &Gc<HashMap<SteelVal, SteelVal>>) -> Result<SteelVal> {
1✔
338
    vec_construct_iter_normal(hashmap.keys().cloned())
3✔
339
}
340

341
/// Returns the values of the given hash map as an immutable vector
342
///
343
/// (hash-values->vector map) -> (vectorof any/c)?
344
///
345
/// * map : hash?
346
///
347
/// # Examples
348
///
349
/// ```scheme
350
/// > (hash-values->vector (hash 'a 10 'b 20))
351
/// => '#(10 20)
352
/// ```
353
#[steel_derive::function(name = "hash-values->vector")]
354
pub fn values_to_vector(hashmap: &Gc<HashMap<SteelVal, SteelVal>>) -> Result<SteelVal> {
1✔
355
    vec_construct_iter_normal(hashmap.values().cloned())
3✔
356
}
357

358
/// Clears the entries out of the existing hashmap.
359
/// Will attempt to reuse the existing memory if there are no other references
360
/// to the hashmap.
361
///
362
/// (hash-clear map) -> hash?
363
///
364
/// * map : hash?
365
///
366
/// # Examples
367
/// ```scheme
368
/// > (hash-clear (hash 'a 10 'b 20))
369
/// => '#hash()
370
/// ```
371
#[steel_derive::function(name = "hash-clear")]
UNCOV
372
pub fn clear(hashmap: &mut SteelVal) -> Result<SteelVal> {
×
UNCOV
373
    if let SteelVal::HashMapV(SteelHashMap(ref mut m)) = hashmap {
×
UNCOV
374
        match Gc::get_mut(m) {
×
UNCOV
375
            Some(m) => {
×
376
                // m.insert(key, value);
UNCOV
377
                m.clear();
×
UNCOV
378
                Ok(std::mem::replace(hashmap, SteelVal::Void))
×
379
            }
UNCOV
380
            None => Ok(SteelVal::HashMapV(Gc::new(HashMap::new()).into())),
×
381
        }
382
    } else {
383
        stop!(TypeMismatch => "hash-clear expected a hashmap, found: {:?}", hashmap);
×
384
    }
385
}
386

387
/// Checks whether the hash map is empty
388
///
389
/// (hash-empty? map) -> bool?
390
///
391
/// * map : hash?
392
///
393
/// # Examples
394
/// ```scheme
395
/// > (hash-empty? (hash 'a 10)) ;; => #f
396
/// > (hash-emptY? (hash)) ;; => #true
397
/// ```
398
#[steel_derive::function(name = "hash-empty?")]
UNCOV
399
pub fn hm_empty(hm: &Gc<HashMap<SteelVal, SteelVal>>) -> Result<SteelVal> {
×
UNCOV
400
    Ok(SteelVal::BoolV(hm.is_empty()))
×
401
}
402

403
/// Constructs the union of two hashmaps, keeping the values
404
/// in the left map when the keys exist in both maps.
405
///
406
/// Will reuse memory where possible.
407
///
408
/// (hash-union l r) -> hash?
409
///
410
/// # Examples
411
/// ```scheme
412
/// > (hash-union (hash 'a 10) (hash 'b 20)) ;; => '#hash((a . 10) (b . 20))
413
/// ```
414
#[steel_derive::function(name = "hash-union")]
415
pub fn hm_union(mut hml: &mut SteelVal, mut hmr: &mut SteelVal) -> Result<SteelVal> {
1,444✔
416
    match (&mut hml, &mut hmr) {
2,888✔
417
        (
418
            SteelVal::HashMapV(SteelHashMap(ref mut l)),
1,444✔
419
            SteelVal::HashMapV(SteelHashMap(ref mut r)),
1,444✔
420
        ) => match (Gc::get_mut(l), Gc::get_mut(r)) {
5,776✔
421
            (None, None) => {
UNCOV
422
                let hml = l.unwrap();
×
UNCOV
423
                let hmr = r.unwrap();
×
UNCOV
424
                Ok(SteelVal::HashMapV(Gc::new(hml.union(hmr)).into()))
×
425
            }
UNCOV
426
            (None, Some(r_map)) => {
×
UNCOV
427
                let right_side_value = std::mem::take(r_map);
×
428

UNCOV
429
                *r_map = l.unwrap().union(right_side_value);
×
430

UNCOV
431
                Ok(std::mem::replace(hmr, SteelVal::Void))
×
432
            }
433
            (Some(l_map), None) => {
×
434
                let left_side_value = std::mem::take(l_map);
×
435

UNCOV
436
                *l_map = left_side_value.union(r.unwrap());
×
437

438
                Ok(std::mem::replace(hml, SteelVal::Void))
×
439
            }
440
            (Some(l_map), Some(r_map)) => {
2,888✔
441
                let left_side_value = std::mem::take(l_map);
4,332✔
442
                let right_side_value = std::mem::take(r_map);
4,332✔
443

444
                *l_map = left_side_value.union(right_side_value);
5,776✔
445

446
                Ok(std::mem::replace(hml, SteelVal::Void))
2,888✔
447
            }
448
        },
449

450
        _ => {
UNCOV
451
            stop!(TypeMismatch => "hash-union expects two hash maps, found: {:?} and {:?}", hml, hmr)
×
452
        }
453
    }
454
}
455

456
#[cfg(test)]
457
mod hashmap_tests {
458
    use super::*;
459

460
    #[cfg(not(feature = "sync"))]
461
    use im_rc::hashmap;
462

463
    #[cfg(not(feature = "sync"))]
464
    use im_rc::vector;
465

466
    #[cfg(all(feature = "sync", not(feature = "imbl")))]
467
    use im::hashmap;
468

469
    #[cfg(all(feature = "sync", not(feature = "imbl")))]
470
    use im::vector;
471

472
    #[cfg(all(feature = "sync", feature = "imbl"))]
473
    use imbl::hashmap;
474

475
    #[cfg(all(feature = "sync", feature = "imbl"))]
476
    use imbl::vector;
477

478
    use crate::rvals::{SteelString, SteelVal::*};
479

480
    #[test]
481
    fn hm_construct_normal() {
482
        let args = [
483
            StringV("foo".into()),
484
            StringV("bar".into()),
485
            StringV("foo2".into()),
486
            StringV("bar2".into()),
487
        ];
488
        let res = hm_construct(&args);
489
        let expected = SteelVal::HashMapV(
490
            Gc::new(hashmap! {
491
                StringV("foo".into()) => StringV("bar".into()),
492
                StringV("foo2".into()) => StringV("bar2".into())
493
            })
494
            .into(),
495
        );
496
        assert_eq!(res.unwrap(), expected);
497
    }
498

499
    #[test]
500
    fn hm_construct_with_duplicates() {
501
        let args = [
502
            StringV("foo".into()),
503
            StringV("bar".into()),
504
            StringV("foo2".into()),
505
            StringV("bar2".into()),
506
            StringV("foo".into()),
507
            StringV("bar".into()),
508
            StringV("foo2".into()),
509
            StringV("bar2".into()),
510
        ];
511
        let res = hm_construct(&args);
512
        let expected = SteelVal::HashMapV(
513
            Gc::new(hashmap! {
514
                StringV("foo".into()) => StringV("bar".into()),
515
                StringV("foo2".into()) => StringV("bar2".into())
516
            })
517
            .into(),
518
        );
519
        assert_eq!(res.unwrap(), expected);
520
    }
521

522
    #[test]
523
    fn hm_insert_from_empty() {
524
        let mut args = [
525
            HashMapV(Gc::new(hashmap![]).into()),
526
            StringV("foo".into()),
527
            StringV("bar".into()),
528
        ];
529
        let res = steel_hash_insert(&mut args);
530
        let expected = SteelVal::HashMapV(
531
            Gc::new(hashmap! {
532
                StringV("foo".into()) => StringV("bar".into())
533
            })
534
            .into(),
535
        );
536
        assert_eq!(res.unwrap(), expected);
537
    }
538

539
    #[test]
540
    fn hm_get_found() {
541
        let args = [
542
            HashMapV(
543
                Gc::new(hashmap! {
544
                    StringV("foo".into()) => StringV("bar".into())
545
                })
546
                .into(),
547
            ),
548
            StringV("foo".into()),
549
        ];
550
        let res = steel_hash_ref(&args);
551
        let expected = StringV("bar".into());
552
        assert_eq!(res.unwrap(), expected);
553
    }
554

555
    #[test]
556
    fn hm_get_error() {
557
        let args = [
558
            HashMapV(
559
                Gc::new(hashmap! {
560
                    StringV("foo".into()) => StringV("bar".into())
561
                })
562
                .into(),
563
            ),
564
            StringV("garbage".into()),
565
        ];
566
        let res = steel_hash_ref(&args);
567
        assert!(res.is_err());
568
    }
569

570
    #[test]
571
    fn hm_try_get_found() {
572
        let args = [
573
            HashMapV(
574
                Gc::new(hashmap! {
575
                    StringV("foo".into()) => StringV("bar".into())
576
                })
577
                .into(),
578
            ),
579
            StringV("foo".into()),
580
        ];
581
        let res = steel_hash_try_get(&args);
582
        let expected = StringV("bar".into());
583
        assert_eq!(res.unwrap(), expected);
584
    }
585

586
    #[test]
587
    fn hm_try_get_error() {
588
        let args = [
589
            HashMapV(
590
                Gc::new(hashmap! {
591
                    StringV("foo".into()) => StringV("bar".into())
592
                })
593
                .into(),
594
            ),
595
            StringV("garbage".into()),
596
        ];
597
        let res = steel_hash_contains(&args);
598
        let expected = SteelVal::BoolV(false);
599
        assert_eq!(res.unwrap(), expected);
600
    }
601

602
    #[test]
603
    fn hm_contains_true() {
604
        let args = [
605
            HashMapV(
606
                Gc::new(hashmap! {
607
                    StringV("foo".into()) => StringV("bar".into())
608
                })
609
                .into(),
610
            ),
611
            StringV("foo".into()),
612
        ];
613
        let res = steel_hash_contains(&args);
614
        let expected = SteelVal::BoolV(true);
615
        assert_eq!(res.unwrap(), expected);
616
    }
617

618
    #[test]
619
    fn hm_contains_false() {
620
        let args = [
621
            HashMapV(
622
                Gc::new(hashmap! {
623
                    StringV("foo".into()) => StringV("bar".into())
624
                })
625
                .into(),
626
            ),
627
            StringV("bar".into()),
628
        ];
629
        let res = steel_hash_contains(&args);
630
        let expected = SteelVal::BoolV(false);
631
        assert_eq!(res.unwrap(), expected);
632
    }
633

634
    #[test]
635
    fn hm_keys_to_vector_normal() {
636
        let args = vec![HashMapV(
637
            Gc::new(hashmap! {
638
                StringV("foo".into()) => StringV("bar".into()),
639
                StringV("bar".into()) => StringV("baz".into()),
640
                StringV("baz".into()) => StringV("quux".into())
641
            })
642
            .into(),
643
        )];
644
        let res = steel_keys_to_vector(&args);
645
        let expected = vector![
646
            SteelVal::StringV("foo".into()),
647
            SteelVal::StringV("bar".into()),
648
            SteelVal::StringV("baz".into()),
649
        ]
650
        .into();
651

652
        // pull out the vectors and sort them
653
        // let unwrapped_res: SteelVal = (*res.unwrap()).clone();
654
        // let unwrapped_expected: SteelVal = (*expected).clone();
655

656
        let mut res_vec_string: Vec<SteelString> = if let SteelVal::VectorV(v) = res.unwrap() {
657
            v.iter()
658
                .map(|x| {
659
                    if let SteelVal::StringV(ref s) = x {
660
                        s.clone()
661
                    } else {
662
                        panic!("test failed")
663
                    }
664
                })
665
                .collect()
666
        } else {
667
            panic!("test failed")
668
        };
669

670
        let mut expected_vec_string: Vec<SteelString> = if let SteelVal::VectorV(v) = expected {
671
            v.iter()
672
                .map(|x| {
673
                    if let SteelVal::StringV(ref s) = x {
674
                        s.clone()
675
                    } else {
676
                        panic!("test failed")
677
                    }
678
                })
679
                .collect()
680
        } else {
681
            panic!("test failed")
682
        };
683

684
        res_vec_string.sort();
685
        expected_vec_string.sort();
686

687
        assert_eq!(res_vec_string, expected_vec_string);
688
    }
689

690
    #[test]
691
    fn hm_values_to_vector_normal() {
692
        let args = vec![HashMapV(
693
            Gc::new(hashmap! {
694
                StringV("foo".into()) => StringV("bar".into()),
695
                StringV("bar".into()) => StringV("baz".into()),
696
                StringV("baz".into()) => StringV("quux".into())
697
            })
698
            .into(),
699
        )];
700
        let res = steel_values_to_vector(&args);
701
        let expected = vector![
702
            SteelVal::StringV("bar".into()),
703
            SteelVal::StringV("baz".into()),
704
            SteelVal::StringV("quux".into()),
705
        ]
706
        .into();
707

708
        // pull out the vectors and sort them
709

710
        let mut res_vec_string: Vec<SteelString> = if let SteelVal::VectorV(v) = res.unwrap() {
711
            v.iter()
712
                .map(|x| {
713
                    if let SteelVal::StringV(ref s) = x {
714
                        s.clone()
715
                    } else {
716
                        panic!("test failed")
717
                    }
718
                })
719
                .collect()
720
        } else {
721
            panic!("test failed")
722
        };
723

724
        let mut expected_vec_string: Vec<SteelString> = if let SteelVal::VectorV(v) = expected {
725
            v.iter()
726
                .map(|x| {
727
                    if let SteelVal::StringV(ref s) = x {
728
                        s.clone()
729
                    } else {
730
                        panic!("test failed")
731
                    }
732
                })
733
                .collect()
734
        } else {
735
            panic!("test failed")
736
        };
737

738
        res_vec_string.sort();
739
        expected_vec_string.sort();
740

741
        assert_eq!(res_vec_string, expected_vec_string);
742
    }
743
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc