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

mattwparas / steel / 19643200669

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

Pull #574

github

web-flow
Merge a59c922d4 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%)

18 existing lines in 4 files now uncovered.

15943 of 32350 relevant lines covered (49.28%)

3471394.66 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
/// Gets the hash code for the given value;
49
///
50
/// (hash-code v) -> integer?
51
///
52
/// * v : hashable?
53
///
54
/// # Examples
55
/// ```scheme
56
/// (hash-code 10) ;; => 16689870864682149525
57
/// (hash-code "hello world") ;; => 12361891819228967546
58
/// ```
59
#[steel_derive::function(name = "hash-code", constant = false)]
NEW
60
pub fn hash_code(arg: &SteelVal) -> Result<SteelVal> {
×
NEW
61
    let mut hasher = DefaultHasher::new();
×
62

NEW
63
    if !arg.is_hashable() {
×
NEW
64
        stop!(TypeMismatch => "value is not hashable: {}", arg);
×
65
    }
66

NEW
67
    arg.hash(&mut hasher);
×
NEW
68
    let value = hasher.finish();
×
69

NEW
70
    if let Ok(v) = isize::try_from(value) {
×
NEW
71
        Ok(SteelVal::IntV(v))
×
72
    } else {
NEW
73
        Ok(SteelVal::BigNum(Gc::new(BigInt::from(value))))
×
74
    }
75
}
76

77
/// Creates an immutable hash table with each given `key` mapped to the following `val`.
78
/// Each key must have a val, so the total number of arguments must be even.
79
///
80
/// (hash key val ...) -> hash?
81
///
82
/// * key : hashable?
83
/// * val : any/c
84
///
85
/// Note: the keys must be hashable.
86
///
87
/// # Examples
88
/// ```scheme
89
/// > (hash 'a 10 'b 20)
90
/// => '#hash((a . 10) (b . 20))
91
/// ```
92
#[steel_derive::native(name = "hash", arity = "AtLeast(0)")]
93
pub fn hm_construct(args: &[SteelVal]) -> Result<SteelVal> {
119,552✔
94
    let mut hm = HashMap::new();
239,104✔
95

96
    let mut arg_iter = args.iter().cloned();
478,208✔
97

98
    loop {
99
        match (arg_iter.next(), arg_iter.next()) {
1,062,420✔
100
            (Some(key), Some(value)) => {
292,106✔
101
                if key.is_hashable() {
438,159✔
102
                    hm.insert(key, value);
438,159✔
103
                } else {
104
                    stop!(TypeMismatch => "hash key not hashable!: {}", key);
×
105
                }
106
            }
107
            (None, None) => break,
119,552✔
108
            _ => {
109
                stop!(ArityMismatch => "hash map must have a value for every key!");
×
110
            }
111
        }
112
    }
113

114
    Ok(SteelVal::HashMapV(Gc::new(hm).into()))
239,104✔
115
}
116

117
pub fn hm_construct_keywords(args: &[SteelVal]) -> Result<SteelVal> {
×
118
    let mut hm = HashMap::new();
×
119

120
    let mut arg_iter = args.iter().cloned();
×
121

122
    loop {
123
        match (arg_iter.next(), arg_iter.next()) {
×
124
            (Some(key), Some(value)) => {
×
125
                if key.is_hashable() {
×
126
                    hm.insert(key, value);
×
127
                } else {
128
                    stop!(TypeMismatch => "hash key not hashable!: {}", key);
×
129
                }
130
            }
131
            (None, None) => break,
×
132
            _ => {
133
                stop!(ArityMismatch => "Missing keyword argument!");
×
134
            }
135
        }
136
    }
137

138
    Ok(SteelVal::HashMapV(Gc::new(hm).into()))
×
139
}
140

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

168
                    Ok(SteelVal::HashMapV(Gc::new(m).into()))
×
169
                }
170
            }
171
        } else {
172
            stop!(TypeMismatch => "hash-remove expects a hash map, found: {:?}", map);
×
173
        }
174
    } else {
175
        stop!(TypeMismatch => "hash key not hashable: {:?}", key)
×
176
    }
177
}
178

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

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

243
/// Gets the `key` from the given `map`. Returns #false if the key does not exist.
244
///
245
/// (hash-try-get map key) -> (or any/c #false)
246
///
247
/// * map : hash?
248
/// * key : any/c
249
///
250
/// # Examples
251
///
252
/// ```scheme
253
/// > (hash-try-get (hash 'a 10 'b 20) 'b) ;; => 20
254
/// > (hash-try-get (hash 'a 10 'b 20) 'does-not-exist) ;; => #false
255
/// ```
256
#[function(name = "hash-try-get")]
257
pub fn hash_try_get(map: &Gc<HashMap<SteelVal, SteelVal>>, key: &SteelVal) -> SteelVal {
4,415✔
258
    match map.get(key) {
8,830✔
259
        Some(v) => v.clone(),
1,893✔
260
        None => SteelVal::BoolV(false),
3,784✔
261
    }
262
}
263

264
/// Returns the number of key value pairs in the map
265
///
266
/// (hash-length map) -> (and/c positive? int?)
267
///
268
/// * map : hash?
269
///
270
/// # Examples
271
///
272
/// ```scheme
273
/// > (hash-length (hash 'a 10 'b 20)) ;; => 2
274
/// ```
275
#[function(name = "hash-length")]
276
pub fn hash_length(map: &Gc<HashMap<SteelVal, SteelVal>>) -> usize {
×
277
    map.len()
×
278
}
279

280
/// Checks whether the given map contains the given key. Key must be hashable.
281
///
282
/// (hash-contains? map key) -> bool?
283
///
284
/// * map : hash?
285
/// * key : hashable?
286
///
287
/// # Example
288
///
289
/// ```scheme
290
/// > (hash-contains? (hash 'a 10 'b 20) 'a) ;; => #true
291
/// > (hash-contains? (hash 'a 10 'b 20) 'not-there) ;; => #false
292
/// ```
293
#[function(name = "hash-contains?")]
294
pub fn hash_contains(map: &Gc<HashMap<SteelVal, SteelVal>>, key: &SteelVal) -> Result<SteelVal> {
3✔
295
    if key.is_hashable() {
6✔
296
        Ok(SteelVal::BoolV(map.contains_key(key)))
6✔
297
    } else {
298
        stop!(TypeMismatch => "hash key not hashable!: {}", key);
×
299
    }
300
}
301

302
/// Returns the keys of the given hash map as a list.
303
///
304
/// (hash-keys->list map) -> (listof hashable?)
305
///
306
/// * map : hash?
307
///
308
/// # Examples
309
///
310
/// ```scheme
311
/// > (hash-keys->list (hash 'a 10 'b 20))
312
/// => '(a b)
313
/// ```
314
#[function(name = "hash-keys->list")]
315
pub fn keys_to_list(hashmap: &Gc<HashMap<SteelVal, SteelVal>>) -> Result<SteelVal> {
×
316
    Ok(SteelVal::ListV(hashmap.keys().cloned().collect()))
×
317
}
318

319
/// Returns the values of the given hash map as a list
320
///
321
/// (hash-values->list map) -> (listof any/c)
322
///
323
/// * map : hash?
324
///
325
/// # Examples
326
///
327
/// ```scheme
328
/// > (hash-values->list (hash 'a 10 'b 20))
329
/// => '(10 20)
330
/// ```
331
#[steel_derive::function(name = "hash-values->list")]
332
pub fn values_to_list(hashmap: &Gc<HashMap<SteelVal, SteelVal>>) -> Result<SteelVal> {
×
333
    Ok(SteelVal::ListV(hashmap.values().cloned().collect()))
×
334
}
335

336
/// Returns the keys of the given hash map as an immutable vector
337
///
338
/// (hash-keys->vector map) -> (vectorof hashable?)
339
///
340
/// * map: hash?
341
///
342
/// # Examples
343
/// ```scheme
344
/// > (hash-keys->vector (hash 'a 10 'b 20))
345
/// => '#(a b)
346
/// ```
347
#[steel_derive::function(name = "hash-keys->vector")]
348
pub fn keys_to_vector(hashmap: &Gc<HashMap<SteelVal, SteelVal>>) -> Result<SteelVal> {
1✔
349
    vec_construct_iter_normal(hashmap.keys().cloned())
3✔
350
}
351

352
/// Returns the values of the given hash map as an immutable vector
353
///
354
/// (hash-values->vector map) -> (vectorof any/c)?
355
///
356
/// * map : hash?
357
///
358
/// # Examples
359
///
360
/// ```scheme
361
/// > (hash-values->vector (hash 'a 10 'b 20))
362
/// => '#(10 20)
363
/// ```
364
#[steel_derive::function(name = "hash-values->vector")]
365
pub fn values_to_vector(hashmap: &Gc<HashMap<SteelVal, SteelVal>>) -> Result<SteelVal> {
1✔
366
    vec_construct_iter_normal(hashmap.values().cloned())
3✔
367
}
368

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

398
/// Checks whether the hash map is empty
399
///
400
/// (hash-empty? map) -> bool?
401
///
402
/// * map : hash?
403
///
404
/// # Examples
405
/// ```scheme
406
/// > (hash-empty? (hash 'a 10)) ;; => #f
407
/// > (hash-emptY? (hash)) ;; => #true
408
/// ```
409
#[steel_derive::function(name = "hash-empty?")]
410
pub fn hm_empty(hm: &Gc<HashMap<SteelVal, SteelVal>>) -> Result<SteelVal> {
×
411
    Ok(SteelVal::BoolV(hm.is_empty()))
×
412
}
413

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

440
                *r_map = l.unwrap().union(right_side_value);
×
441

442
                Ok(std::mem::replace(hmr, SteelVal::Void))
×
443
            }
444
            (Some(l_map), None) => {
×
445
                let left_side_value = std::mem::take(l_map);
×
446

447
                *l_map = left_side_value.union(r.unwrap());
×
448

449
                Ok(std::mem::replace(hml, SteelVal::Void))
×
450
            }
451
            (Some(l_map), Some(r_map)) => {
2,888✔
452
                let left_side_value = std::mem::take(l_map);
4,332✔
453
                let right_side_value = std::mem::take(r_map);
4,332✔
454

455
                *l_map = left_side_value.union(right_side_value);
5,776✔
456

457
                Ok(std::mem::replace(hml, SteelVal::Void))
2,888✔
458
            }
459
        },
460

461
        _ => {
462
            stop!(TypeMismatch => "hash-union expects two hash maps, found: {:?} and {:?}", hml, hmr)
×
463
        }
464
    }
465
}
466

467
#[cfg(test)]
468
mod hashmap_tests {
469
    use super::*;
470

471
    #[cfg(not(feature = "sync"))]
472
    use im_rc::hashmap;
473

474
    #[cfg(not(feature = "sync"))]
475
    use im_rc::vector;
476

477
    #[cfg(all(feature = "sync", not(feature = "imbl")))]
478
    use im::hashmap;
479

480
    #[cfg(all(feature = "sync", not(feature = "imbl")))]
481
    use im::vector;
482

483
    #[cfg(all(feature = "sync", feature = "imbl"))]
484
    use imbl::hashmap;
485

486
    #[cfg(all(feature = "sync", feature = "imbl"))]
487
    use imbl::vector;
488

489
    use crate::rvals::{SteelString, SteelVal::*};
490

491
    #[test]
492
    fn hm_construct_normal() {
493
        let args = [
494
            StringV("foo".into()),
495
            StringV("bar".into()),
496
            StringV("foo2".into()),
497
            StringV("bar2".into()),
498
        ];
499
        let res = hm_construct(&args);
500
        let expected = SteelVal::HashMapV(
501
            Gc::new(hashmap! {
502
                StringV("foo".into()) => StringV("bar".into()),
503
                StringV("foo2".into()) => StringV("bar2".into())
504
            })
505
            .into(),
506
        );
507
        assert_eq!(res.unwrap(), expected);
508
    }
509

510
    #[test]
511
    fn hm_construct_with_duplicates() {
512
        let args = [
513
            StringV("foo".into()),
514
            StringV("bar".into()),
515
            StringV("foo2".into()),
516
            StringV("bar2".into()),
517
            StringV("foo".into()),
518
            StringV("bar".into()),
519
            StringV("foo2".into()),
520
            StringV("bar2".into()),
521
        ];
522
        let res = hm_construct(&args);
523
        let expected = SteelVal::HashMapV(
524
            Gc::new(hashmap! {
525
                StringV("foo".into()) => StringV("bar".into()),
526
                StringV("foo2".into()) => StringV("bar2".into())
527
            })
528
            .into(),
529
        );
530
        assert_eq!(res.unwrap(), expected);
531
    }
532

533
    #[test]
534
    fn hm_insert_from_empty() {
535
        let mut args = [
536
            HashMapV(Gc::new(hashmap![]).into()),
537
            StringV("foo".into()),
538
            StringV("bar".into()),
539
        ];
540
        let res = steel_hash_insert(&mut args);
541
        let expected = SteelVal::HashMapV(
542
            Gc::new(hashmap! {
543
                StringV("foo".into()) => StringV("bar".into())
544
            })
545
            .into(),
546
        );
547
        assert_eq!(res.unwrap(), expected);
548
    }
549

550
    #[test]
551
    fn hm_get_found() {
552
        let args = [
553
            HashMapV(
554
                Gc::new(hashmap! {
555
                    StringV("foo".into()) => StringV("bar".into())
556
                })
557
                .into(),
558
            ),
559
            StringV("foo".into()),
560
        ];
561
        let res = steel_hash_ref(&args);
562
        let expected = StringV("bar".into());
563
        assert_eq!(res.unwrap(), expected);
564
    }
565

566
    #[test]
567
    fn hm_get_error() {
568
        let args = [
569
            HashMapV(
570
                Gc::new(hashmap! {
571
                    StringV("foo".into()) => StringV("bar".into())
572
                })
573
                .into(),
574
            ),
575
            StringV("garbage".into()),
576
        ];
577
        let res = steel_hash_ref(&args);
578
        assert!(res.is_err());
579
    }
580

581
    #[test]
582
    fn hm_try_get_found() {
583
        let args = [
584
            HashMapV(
585
                Gc::new(hashmap! {
586
                    StringV("foo".into()) => StringV("bar".into())
587
                })
588
                .into(),
589
            ),
590
            StringV("foo".into()),
591
        ];
592
        let res = steel_hash_try_get(&args);
593
        let expected = StringV("bar".into());
594
        assert_eq!(res.unwrap(), expected);
595
    }
596

597
    #[test]
598
    fn hm_try_get_error() {
599
        let args = [
600
            HashMapV(
601
                Gc::new(hashmap! {
602
                    StringV("foo".into()) => StringV("bar".into())
603
                })
604
                .into(),
605
            ),
606
            StringV("garbage".into()),
607
        ];
608
        let res = steel_hash_contains(&args);
609
        let expected = SteelVal::BoolV(false);
610
        assert_eq!(res.unwrap(), expected);
611
    }
612

613
    #[test]
614
    fn hm_contains_true() {
615
        let args = [
616
            HashMapV(
617
                Gc::new(hashmap! {
618
                    StringV("foo".into()) => StringV("bar".into())
619
                })
620
                .into(),
621
            ),
622
            StringV("foo".into()),
623
        ];
624
        let res = steel_hash_contains(&args);
625
        let expected = SteelVal::BoolV(true);
626
        assert_eq!(res.unwrap(), expected);
627
    }
628

629
    #[test]
630
    fn hm_contains_false() {
631
        let args = [
632
            HashMapV(
633
                Gc::new(hashmap! {
634
                    StringV("foo".into()) => StringV("bar".into())
635
                })
636
                .into(),
637
            ),
638
            StringV("bar".into()),
639
        ];
640
        let res = steel_hash_contains(&args);
641
        let expected = SteelVal::BoolV(false);
642
        assert_eq!(res.unwrap(), expected);
643
    }
644

645
    #[test]
646
    fn hm_keys_to_vector_normal() {
647
        let args = vec![HashMapV(
648
            Gc::new(hashmap! {
649
                StringV("foo".into()) => StringV("bar".into()),
650
                StringV("bar".into()) => StringV("baz".into()),
651
                StringV("baz".into()) => StringV("quux".into())
652
            })
653
            .into(),
654
        )];
655
        let res = steel_keys_to_vector(&args);
656
        let expected = vector![
657
            SteelVal::StringV("foo".into()),
658
            SteelVal::StringV("bar".into()),
659
            SteelVal::StringV("baz".into()),
660
        ]
661
        .into();
662

663
        // pull out the vectors and sort them
664
        // let unwrapped_res: SteelVal = (*res.unwrap()).clone();
665
        // let unwrapped_expected: SteelVal = (*expected).clone();
666

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

681
        let mut expected_vec_string: Vec<SteelString> = if let SteelVal::VectorV(v) = expected {
682
            v.iter()
683
                .map(|x| {
684
                    if let SteelVal::StringV(ref s) = x {
685
                        s.clone()
686
                    } else {
687
                        panic!("test failed")
688
                    }
689
                })
690
                .collect()
691
        } else {
692
            panic!("test failed")
693
        };
694

695
        res_vec_string.sort();
696
        expected_vec_string.sort();
697

698
        assert_eq!(res_vec_string, expected_vec_string);
699
    }
700

701
    #[test]
702
    fn hm_values_to_vector_normal() {
703
        let args = vec![HashMapV(
704
            Gc::new(hashmap! {
705
                StringV("foo".into()) => StringV("bar".into()),
706
                StringV("bar".into()) => StringV("baz".into()),
707
                StringV("baz".into()) => StringV("quux".into())
708
            })
709
            .into(),
710
        )];
711
        let res = steel_values_to_vector(&args);
712
        let expected = vector![
713
            SteelVal::StringV("bar".into()),
714
            SteelVal::StringV("baz".into()),
715
            SteelVal::StringV("quux".into()),
716
        ]
717
        .into();
718

719
        // pull out the vectors and sort them
720

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

735
        let mut expected_vec_string: Vec<SteelString> = if let SteelVal::VectorV(v) = expected {
736
            v.iter()
737
                .map(|x| {
738
                    if let SteelVal::StringV(ref s) = x {
739
                        s.clone()
740
                    } else {
741
                        panic!("test failed")
742
                    }
743
                })
744
                .collect()
745
        } else {
746
            panic!("test failed")
747
        };
748

749
        res_vec_string.sort();
750
        expected_vec_string.sort();
751

752
        assert_eq!(res_vec_string, expected_vec_string);
753
    }
754
}
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