1use crate::{
4 AttrId, EntityId, TxId, Value,
5 encoding::{DecodeError, Encodable, decode_entity_id, decode_u64, decode_value},
6};
7
8#[derive(Clone, Debug, Eq, PartialEq)]
10pub struct Datom {
11 pub e: EntityId,
13 pub a: AttrId,
15 pub v: Value,
17 pub tx: TxId,
19 pub added: bool,
21}
22
23#[derive(Clone, Copy, Debug, Eq, PartialEq)]
25pub enum IndexOrder {
26 Eavt,
28 Aevt,
30 Avet,
32 Vaet,
34}
35
36impl Datom {
37 #[must_use]
39 pub fn key(&self, order: IndexOrder) -> Vec<u8> {
40 let mut out = Vec::new();
41 match order {
42 IndexOrder::Eavt => {
43 self.e.encode_into(&mut out);
44 self.a.encode_into(&mut out);
45 self.v.encode_into(&mut out);
46 }
47 IndexOrder::Aevt => {
48 self.a.encode_into(&mut out);
49 self.e.encode_into(&mut out);
50 self.v.encode_into(&mut out);
51 }
52 IndexOrder::Avet => {
53 self.a.encode_into(&mut out);
54 self.v.encode_into(&mut out);
55 self.e.encode_into(&mut out);
56 }
57 IndexOrder::Vaet => {
58 self.v.encode_into(&mut out);
59 self.a.encode_into(&mut out);
60 self.e.encode_into(&mut out);
61 }
62 }
63 encode_tx_added(self.tx, self.added, &mut out);
64 out
65 }
66
67 pub fn from_key(order: IndexOrder, input: &[u8]) -> Result<Self, DecodeError> {
72 let mut offset = 0;
73 let entity = |offset: &mut usize| {
74 let (value, used) = decode_entity_id(&input[*offset..])?;
75 *offset += used;
76 Ok::<_, DecodeError>(value)
77 };
78 let (e, a, v) = match order {
79 IndexOrder::Eavt => {
80 let e = entity(&mut offset)?;
81 let a = entity(&mut offset)?;
82 let (v, used) = decode_value(&input[offset..])?;
83 offset += used;
84 (e, a, v)
85 }
86 IndexOrder::Aevt => {
87 let a = entity(&mut offset)?;
88 let e = entity(&mut offset)?;
89 let (v, used) = decode_value(&input[offset..])?;
90 offset += used;
91 (e, a, v)
92 }
93 IndexOrder::Avet => {
94 let a = entity(&mut offset)?;
95 let (v, used) = decode_value(&input[offset..])?;
96 offset += used;
97 let e = entity(&mut offset)?;
98 (e, a, v)
99 }
100 IndexOrder::Vaet => {
101 let (v, used) = decode_value(&input[offset..])?;
102 offset += used;
103 let a = entity(&mut offset)?;
104 let e = entity(&mut offset)?;
105 (e, a, v)
106 }
107 };
108 let (tx_added, used) = decode_u64(&input[offset..])?;
109 offset += used;
110 if offset != input.len() {
111 return Err(DecodeError::Trailing);
112 }
113 Ok(Self {
114 e,
115 a,
116 v,
117 tx: EntityId::from_raw(tx_added >> 1),
118 added: tx_added & 1 == 1,
119 })
120 }
121}
122
123fn encode_tx_added(tx: TxId, added: bool, out: &mut Vec<u8>) {
124 ((tx.raw() << 1) | u64::from(added)).encode_into(out);
125}
126
127#[cfg(test)]
128mod tests {
129 use super::*;
130
131 #[test]
132 fn covering_keys_round_trip() {
133 let datom = Datom {
134 e: EntityId::from_raw(42),
135 a: EntityId::from_raw(7),
136 v: Value::Str("a\0z".into()),
137 tx: EntityId::from_raw(19),
138 added: true,
139 };
140 for order in [
141 IndexOrder::Eavt,
142 IndexOrder::Aevt,
143 IndexOrder::Avet,
144 IndexOrder::Vaet,
145 ] {
146 let key = datom.key(order);
147 assert_eq!(Datom::from_key(order, &key), Ok(datom.clone()));
148 }
149 }
150
151 #[test]
152 fn covering_key_rejects_trailing_bytes() {
153 let datom = Datom {
154 e: EntityId::from_raw(1),
155 a: EntityId::from_raw(2),
156 v: Value::Long(3),
157 tx: EntityId::from_raw(4),
158 added: false,
159 };
160 let mut key = datom.key(IndexOrder::Eavt);
161 key.push(0);
162 assert_eq!(
163 Datom::from_key(IndexOrder::Eavt, &key),
164 Err(DecodeError::Trailing)
165 );
166 }
167}