1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
use crate::hash::Hash128;

// ----------------------------------------------------------------------------

/// The key of a table, or an array index.
///
/// This is a variant of [`EntityPathPart`][crate::EntityPathPart] which makes up [`EntityPath`][crate::EntityPath].
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub enum Index {
    /// For arrays, assumed to be dense (0, 1, 2, …).
    Sequence(u64),

    /// Any integer, e.g. a hash or an arbitrary identifier.
    Integer(i128),

    /// UUID/GUID
    Uuid(uuid::Uuid),

    /// Anything goes.
    String(String),
}

#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub enum BatchIndex {
    /// Many batches use implicit sequential-indexing
    SequentialIndex(usize),

    /// Some batches want to provide explicit indices
    FullIndex(Vec<Index>),
}

impl BatchIndex {
    pub fn len(&self) -> usize {
        match &self {
            BatchIndex::SequentialIndex(sz) => *sz,
            BatchIndex::FullIndex(vec) => vec.len(),
        }
    }

    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }
}

impl Index {
    #[inline]
    pub fn hash(&self) -> IndexHash {
        IndexHash::hash(self)
    }
}

impl std::fmt::Display for Index {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Sequence(seq) => format!("#{seq}").fmt(f),
            Self::Integer(value) => value.fmt(f),
            Self::Uuid(value) => value.fmt(f),
            Self::String(value) => format!("{value:?}").fmt(f), // put it in quotes
        }
    }
}

crate::impl_into_enum!(String, Index, String);

impl From<&str> for Index {
    #[inline]
    fn from(s: &str) -> Self {
        Self::String(s.into())
    }
}

// ----------------------------------------------------------------------------

/// A 128 bit hash of [`Index`] with negligible risk of collision.
#[derive(Copy, Clone, Eq)]
pub struct IndexHash(Hash128);

impl IndexHash {
    pub const NONE: IndexHash = Self(Hash128::ZERO);

    #[inline]
    pub fn hash(index: &Index) -> Self {
        Self(Hash128::hash(index))
    }

    #[inline]
    pub fn is_none(&self) -> bool {
        self.0 == Hash128::ZERO
    }

    #[inline]
    pub fn is_some(&self) -> bool {
        self.0 != Hash128::ZERO
    }

    #[inline]
    pub fn hash64(&self) -> u64 {
        self.0.hash64()
    }

    #[inline]
    pub fn first64(&self) -> u64 {
        self.0.first64()
    }

    #[inline]
    pub fn second64(&self) -> u64 {
        self.0.second64()
    }
}

impl std::hash::Hash for IndexHash {
    #[inline]
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        state.write_u64(self.0.hash64());
    }
}

impl std::cmp::PartialEq for IndexHash {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        self.0 == other.0
    }
}

impl nohash_hasher::IsEnabled for IndexHash {}

impl std::fmt::Debug for IndexHash {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(&format!(
            "IndexHash({:016X}{:016X})",
            self.0.first64(),
            self.0.second64()
        ))
    }
}