Skip to main content

urushi/
key.rs

1//! Identity for the things an application names.
2
3use std::any::TypeId;
4use std::collections::hash_map::DefaultHasher;
5use std::fmt;
6use std::hash::{Hash, Hasher};
7
8/// The identity of something an application names, built from any hashable
9/// value.
10///
11/// A key carries the value's type as well as its hash, so keys built from
12/// equal values of the same type are equal and keys of different types never
13/// are. An application's own enums are therefore its natural keys, and one
14/// name cannot collide with another concern's.
15///
16/// ```
17/// use urushi::Key;
18///
19/// #[derive(Hash)]
20/// enum Pane {
21///     Preview,
22///     Chart,
23/// }
24///
25/// assert_eq!(Key::of(&Pane::Preview), Key::of(&Pane::Preview));
26/// assert_ne!(Key::of(&Pane::Preview), Key::of(&Pane::Chart));
27/// assert_eq!(Key::from("preview"), Key::of(&"preview"));
28/// assert_eq!(format!("{:?}", Key::from("preview")), "Key(preview)");
29/// ```
30///
31/// The value itself is not kept: a key is a type and a hash, which is what
32/// lets it name anything without owning it. Two distinct values of one type
33/// whose hashes collide are therefore one key, as they are in any hash map.
34///
35/// What it does keep is a label to print, and only one that costs nothing to
36/// hold: the literal for a key built from one, and the type's name otherwise.
37/// A key stays `Copy`, and a log line says which key rather than which hash.
38#[derive(Clone, Copy, Eq)]
39pub struct Key {
40    type_id: TypeId,
41    hash: u64,
42    /// What to call this key in a log. Not part of its identity: two keys of
43    /// one value are one key however each was built.
44    label: &'static str,
45}
46
47impl Key {
48    /// Builds the key of `value`.
49    pub fn of<T: Hash + 'static>(value: &T) -> Self {
50        Self::new(TypeId::of::<T>(), value, std::any::type_name::<T>())
51    }
52
53    fn new<T: Hash + ?Sized>(type_id: TypeId, value: &T, label: &'static str) -> Self {
54        let mut hasher = DefaultHasher::new();
55        value.hash(&mut hasher);
56        Self {
57            type_id,
58            hash: hasher.finish(),
59            label,
60        }
61    }
62}
63
64impl From<&'static str> for Key {
65    fn from(name: &'static str) -> Self {
66        Self::new(TypeId::of::<&'static str>(), &name, name)
67    }
68}
69
70impl PartialEq for Key {
71    fn eq(&self, other: &Self) -> bool {
72        self.type_id == other.type_id && self.hash == other.hash
73    }
74}
75
76impl Hash for Key {
77    fn hash<H: Hasher>(&self, hasher: &mut H) {
78        self.type_id.hash(hasher);
79        self.hash.hash(hasher);
80    }
81}
82
83impl fmt::Debug for Key {
84    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
85        write!(formatter, "Key({})", self.label)
86    }
87}