use interpretthis::{InterpreterError, Value, ValueKey, shared_list};
#[test]
fn to_key_integral_float_shares_the_int_slot() {
let from_float = Value::Float(2.0).to_key().expect("float is hashable");
let from_int = Value::Int(2).to_key().expect("int is hashable");
assert_eq!(from_float, from_int);
assert!(matches!(from_float, ValueKey::Float(_)));
use std::hash::{BuildHasher as _, RandomState};
let hasher = RandomState::new();
assert_eq!(hasher.hash_one(&from_float), hasher.hash_one(&from_int));
let fractional = Value::Float(2.5).to_key().expect("float is hashable");
assert_ne!(fractional, from_int);
}
#[test]
fn to_key_rejects_unhashable_values() {
let err =
Value::List(shared_list(vec![Value::Int(1)])).to_key().expect_err("a list is not hashable");
match err {
InterpreterError::TypeError(msg) => {
assert!(msg.contains("unhashable type"), "unexpected message: {msg}");
assert!(msg.contains("list"), "message should name the type: {msg}");
}
other => panic!("expected TypeError, got {other:?}"),
}
}
#[test]
fn to_key_round_trips_through_to_value() {
for value in [
Value::None,
Value::Bool(true),
Value::Int(-7),
Value::String("id".into()),
Value::Tuple(vec![Value::Int(1), Value::String("x".into())]),
] {
let key = value.to_key().expect("hashable");
assert_eq!(key.to_value(), value, "round trip failed for {value:?}");
}
}