Skip to main content

bark/persist/adaptor/
sort.rs

1
2use bitcoin::Amount;
3use chrono::{DateTime, Local};
4
5use crate::vtxo::VtxoStateKind;
6
7
8/// Opaque sort key encoded as bytes for lexicographic comparison.
9///
10/// The encoding ensures that database-level `ORDER BY sort_key ASC` produces
11/// the correct logical ordering. Use [`SortKeyBuilder`] to construct composite
12/// keys with mixed ascending/descending fields.
13///
14/// # Database Storage
15///
16/// Store as `BLOB` (SQLite), `BYTEA` (Postgres), or raw bytes in NoSQL stores.
17/// Create an index on `(partition, sort_key)` for efficient range scans.
18#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
19pub struct SortKey(Vec<u8>);
20
21impl SortKey {
22	/// Returns the raw bytes for database storage.
23	pub fn as_bytes(&self) -> &[u8] {
24		&self.0
25	}
26
27	/// Constructs a sort key from raw bytes (for database retrieval).
28	pub fn from_bytes(bytes: Vec<u8>) -> Self {
29		Self(bytes)
30	}
31
32	/// Creates a builder for composite sort keys.
33	pub fn builder() -> SortKeyBuilder {
34		SortKeyBuilder(Vec::new())
35	}
36
37	/// Creates a sort key from a single u32, ascending order.
38	pub fn u32_asc(n: u32) -> Self {
39		Self::builder().u32_asc(n).build()
40	}
41
42	/// Creates a sort key from a single u64, descending order.
43	pub fn u64_desc(n: u64) -> Self {
44		Self::builder().u64_desc(n).build()
45	}
46}
47
48/// Builder for constructing composite sort keys with multiple fields.
49///
50/// # Example
51///
52/// ```rust
53/// # use bark::persist::adaptor::SortKey;
54///
55/// let height = 100;
56/// let amount = 1000;
57///
58/// // Sort by height ascending, then amount descending
59/// let key = SortKey::builder()
60///     .u32_asc(height)
61///     .u64_desc(amount)
62///     .build();
63/// ```
64#[derive(Debug, Clone, Default)]
65pub struct SortKeyBuilder(Vec<u8>);
66
67impl SortKeyBuilder {
68	pub fn u8_asc(mut self, n: u8) -> Self {
69		self.0.push(n);
70		self
71	}
72
73	/// Appends a u64 in ascending order.
74	pub fn u32_asc(mut self, n: u32) -> Self {
75		self.0.extend_from_slice(&n.to_be_bytes());
76		self
77	}
78
79	/// Appends a u64 in descending order.
80	pub fn u64_desc(mut self, n: u64) -> Self {
81		self.0.extend_from_slice(&(!n).to_be_bytes());
82		self
83	}
84
85	/// Builds the final sort key.
86	pub fn build(self) -> SortKey {
87		SortKey(self.0)
88	}
89}
90
91pub(crate) fn vtxo_sort_key(vtxo_state: VtxoStateKind, expiry_height: u32, amount: Amount) -> SortKey {
92	// Sort by state ASC, then expiry_height ASC, then amount DESC
93	// This prioritizes VTXOs that expire sooner and are larger
94	SortKey::builder()
95		.u8_asc(vtxo_state.as_byte())
96		.u32_asc(expiry_height)
97		.u64_desc(amount.to_sat())
98		.build()
99}
100
101pub(crate) fn movement_sort_key(created_at: &DateTime<Local>) -> SortKey {
102	// Sort by created_at DESC (most recent first)
103	SortKey::u64_desc(created_at.timestamp_millis() as u64)
104}
105
106#[cfg(test)]
107mod tests {
108	use super::*;
109
110	#[test]
111	fn sort_key_u64() {
112		let k1 = SortKey::builder().u32_asc(1).build();
113		let k2 = SortKey::builder().u32_asc(2).build();
114		let k3 = SortKey::builder().u32_asc(3).build();
115
116		assert!(k1 < k2);
117		assert!(k2 < k3);
118		assert!(k1 < k3);
119	}
120
121	#[test]
122	fn sort_key_u64_desc() {
123		let k1 = SortKey::builder().u64_desc(1).build();
124		let k2 = SortKey::builder().u64_desc(2).build();
125		let k3 = SortKey::builder().u64_desc(3).build();
126
127		assert!(k1 > k2);
128		assert!(k2 > k3);
129		assert!(k1 > k3);
130	}
131
132	#[test]
133	fn sort_key_composite() {
134		// Sort by height ASC, then amount DESC
135		let make_key = |height: u32, amount: u64| {
136			SortKey::builder()
137				.u32_asc(height)
138				.u64_desc(amount)
139				.build()
140		};
141
142		// Same height, higher amount should come first
143		let k1 = make_key(100, 1000);
144		let k2 = make_key(100, 500);
145		assert!(k1 < k2); // 1000 DESC < 500 DESC
146
147		// Lower height comes first regardless of amount
148		let k3 = make_key(50, 100);
149		assert!(k3 < k1);
150		assert!(k3 < k2);
151
152		// Higher height comes first regardless of amount
153		let k4 = make_key(150, 100);
154		assert!(k4 > k1);
155		assert!(k4 > k2);
156		assert!(k4 > k3);
157	}
158}