Skip to main content

reifydb_core/key/
variant_handler.rs

1// SPDX-License-Identifier: AGPL-3.0-or-later
2// Copyright (c) 2025 ReifyDB
3
4use reifydb_type::value::sumtype::SumTypeId;
5
6use crate::{
7	encoded::key::{EncodedKey, EncodedKeyRange},
8	interface::catalog::id::{HandlerId, NamespaceId},
9	key::{EncodableKey, KeyKind},
10	util::encoding::keycode::{deserializer::KeyDeserializer, serializer::KeySerializer},
11};
12
13const VERSION: u8 = 1;
14
15/// Key for looking up handlers by the variant they handle.
16/// Supports range scans for DISPATCH to find all handlers for a given (namespace, sumtype, variant_tag).
17#[derive(Debug, Clone, PartialEq)]
18pub struct VariantHandlerKey {
19	pub namespace: NamespaceId,
20	pub sumtype: SumTypeId,
21	pub variant_tag: u8,
22	pub handler: HandlerId,
23}
24
25impl VariantHandlerKey {
26	pub fn new(namespace: NamespaceId, sumtype: SumTypeId, variant_tag: u8, handler: HandlerId) -> Self {
27		Self {
28			namespace,
29			sumtype,
30			variant_tag,
31			handler,
32		}
33	}
34
35	pub fn encoded(
36		namespace: impl Into<NamespaceId>,
37		sumtype: impl Into<SumTypeId>,
38		variant_tag: u8,
39		handler: impl Into<HandlerId>,
40	) -> EncodedKey {
41		Self::new(namespace.into(), sumtype.into(), variant_tag, handler.into()).encode()
42	}
43
44	/// Range scan for all handlers of a specific variant.
45	pub fn variant_scan(namespace: NamespaceId, sumtype: SumTypeId, variant_tag: u8) -> EncodedKeyRange {
46		EncodedKeyRange::start_end(
47			Some(Self::variant_start(namespace, sumtype, variant_tag)),
48			Some(Self::variant_end(namespace, sumtype, variant_tag)),
49		)
50	}
51
52	fn variant_start(namespace: NamespaceId, sumtype: SumTypeId, variant_tag: u8) -> EncodedKey {
53		let mut serializer = KeySerializer::with_capacity(19);
54		serializer
55			.extend_u8(VERSION)
56			.extend_u8(Self::KIND as u8)
57			.extend_u64(namespace)
58			.extend_u64(sumtype)
59			.extend_u8(variant_tag);
60		serializer.to_encoded_key()
61	}
62
63	fn variant_end(namespace: NamespaceId, sumtype: SumTypeId, variant_tag: u8) -> EncodedKey {
64		let mut serializer = KeySerializer::with_capacity(19);
65		serializer
66			.extend_u8(VERSION)
67			.extend_u8(Self::KIND as u8)
68			.extend_u64(namespace)
69			.extend_u64(sumtype)
70			.extend_u8(variant_tag.wrapping_sub(1));
71		serializer.to_encoded_key()
72	}
73}
74
75impl EncodableKey for VariantHandlerKey {
76	const KIND: KeyKind = KeyKind::VariantHandler;
77
78	fn encode(&self) -> EncodedKey {
79		let mut serializer = KeySerializer::with_capacity(27);
80		serializer
81			.extend_u8(VERSION)
82			.extend_u8(Self::KIND as u8)
83			.extend_u64(self.namespace)
84			.extend_u64(self.sumtype)
85			.extend_u8(self.variant_tag)
86			.extend_u64(self.handler);
87		serializer.to_encoded_key()
88	}
89
90	fn decode(key: &EncodedKey) -> Option<Self> {
91		let mut de = KeyDeserializer::from_bytes(key.as_slice());
92
93		let version = de.read_u8().ok()?;
94		if version != VERSION {
95			return None;
96		}
97
98		let kind: KeyKind = de.read_u8().ok()?.try_into().ok()?;
99		if kind != Self::KIND {
100			return None;
101		}
102
103		let namespace = de.read_u64().ok()?;
104		let sumtype = de.read_u64().ok()?;
105		let variant_tag = de.read_u8().ok()?;
106		let handler = de.read_u64().ok()?;
107
108		Some(Self {
109			namespace: NamespaceId(namespace),
110			sumtype: SumTypeId(sumtype),
111			variant_tag,
112			handler: HandlerId(handler),
113		})
114	}
115}
116
117#[cfg(test)]
118pub mod tests {
119	use std::ops::Bound;
120
121	use reifydb_type::value::sumtype::SumTypeId;
122
123	use super::{EncodableKey, VariantHandlerKey};
124	use crate::interface::catalog::id::{HandlerId, NamespaceId};
125
126	#[test]
127	fn test_encode_decode() {
128		let key = VariantHandlerKey {
129			namespace: NamespaceId(0xABCD),
130			sumtype: SumTypeId(0x1234),
131			variant_tag: 5,
132			handler: HandlerId(0x6789),
133		};
134		let encoded = key.encode();
135		let expected: Vec<u8> = vec![
136			0xFE, // version
137			0xCF, // kind (VariantHandler = 0x30, encoded as 0xFF ^ 0x30)
138			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x54, 0x32, // namespace 0xABCD
139			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xED, 0xCB, // sumtype 0x1234
140			0xFA, // variant_tag 5
141			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x98, 0x76, // handler 0x6789
142		];
143		assert_eq!(encoded.as_slice(), expected);
144
145		let decoded = VariantHandlerKey::decode(&encoded).unwrap();
146		assert_eq!(decoded.namespace, NamespaceId(0xABCD));
147		assert_eq!(decoded.sumtype, SumTypeId(0x1234));
148		assert_eq!(decoded.variant_tag, 5);
149		assert_eq!(decoded.handler, HandlerId(0x6789));
150	}
151
152	#[test]
153	fn test_order_preserving() {
154		let key1 = VariantHandlerKey {
155			namespace: NamespaceId(1),
156			sumtype: SumTypeId(5),
157			variant_tag: 3,
158			handler: HandlerId(100),
159		};
160		let key2 = VariantHandlerKey {
161			namespace: NamespaceId(1),
162			sumtype: SumTypeId(5),
163			variant_tag: 3,
164			handler: HandlerId(200),
165		};
166		let key3 = VariantHandlerKey {
167			namespace: NamespaceId(1),
168			sumtype: SumTypeId(5),
169			variant_tag: 4,
170			handler: HandlerId(1),
171		};
172		let key4 = VariantHandlerKey {
173			namespace: NamespaceId(2),
174			sumtype: SumTypeId(1),
175			variant_tag: 0,
176			handler: HandlerId(1),
177		};
178
179		let encoded1 = key1.encode();
180		let encoded2 = key2.encode();
181		let encoded3 = key3.encode();
182		let encoded4 = key4.encode();
183
184		assert!(encoded4 < encoded3, "ordering not preserved");
185		assert!(encoded3 < encoded2, "ordering not preserved");
186		assert!(encoded2 < encoded1, "ordering not preserved");
187	}
188
189	#[test]
190	fn test_variant_scan() {
191		let ns = NamespaceId(1);
192		let st = SumTypeId(10);
193		let tag = 5u8;
194
195		let range = VariantHandlerKey::variant_scan(ns, st, tag);
196		let start = match &range.start {
197			Bound::Included(k) | Bound::Excluded(k) => k,
198			Bound::Unbounded => panic!("expected bounded start"),
199		};
200		let end = match &range.end {
201			Bound::Included(k) | Bound::Excluded(k) => k,
202			Bound::Unbounded => panic!("expected bounded end"),
203		};
204
205		// A key for this exact variant should fall within the range
206		let key = VariantHandlerKey {
207			namespace: ns,
208			sumtype: st,
209			variant_tag: tag,
210			handler: HandlerId(42),
211		};
212		let encoded = key.encode();
213		assert!(encoded.as_slice() >= start.as_slice());
214		assert!(encoded.as_slice() <= end.as_slice());
215
216		// A key for a higher variant_tag encodes to smaller bytes and is outside the range
217		let other = VariantHandlerKey {
218			namespace: ns,
219			sumtype: st,
220			variant_tag: tag + 1,
221			handler: HandlerId(42),
222		};
223		let other_encoded = other.encode();
224		assert!(other_encoded.as_slice() < start.as_slice());
225	}
226}