1use std::{fmt::Display, str::FromStr};
2
3use borsh::{BorshDeserialize, BorshSerialize};
4use bytemuck::{Pod, Zeroable};
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Hash, Ord, PartialOrd, Pod, Zeroable)]
8#[repr(C)]
9pub struct RuneId {
10 pub block: u64,
11 pub tx: u32,
12 _padding: [u8; 4],
13}
14
15impl RuneId {
16 pub const BTC: Self = RuneId {
17 block: 0,
18 tx: 0,
19 _padding: [0; 4],
20 };
21
22 pub fn new(block: u64, tx: u32) -> Self {
23 RuneId {
24 block,
25 tx,
26 _padding: [0; 4],
27 }
28 }
29
30 pub fn to_bytes(&self) -> [u8; 12] {
32 let mut result = [0u8; 12];
33 result[0..8].copy_from_slice(&self.block.to_le_bytes());
34 result[8..12].copy_from_slice(&self.tx.to_le_bytes());
35 result
36 }
37
38 pub fn get_sorted_rune_ids(rune0: &RuneId, rune1: &RuneId) -> ([u8; 12], [u8; 12]) {
46 let rune0_bytes = rune0.to_bytes();
47 let rune1_bytes = rune1.to_bytes();
48 if rune0_bytes <= rune1_bytes {
49 (rune0_bytes, rune1_bytes)
50 } else {
51 (rune1_bytes, rune0_bytes)
52 }
53 }
54
55 pub fn delta(self, next: RuneId) -> Option<(u128, u128)> {
57 let block = next.block.checked_sub(self.block)?;
58
59 let tx = if block == 0 {
60 next.tx.checked_sub(self.tx)?
61 } else {
62 next.tx
63 };
64
65 Some((block.into(), tx.into()))
66 }
67
68 pub fn next(self, block: u128, tx: u128) -> Option<RuneId> {
70 Some(RuneId::new(
71 self.block.checked_add(block.try_into().ok()?)?,
72 if block == 0 {
73 self.tx.checked_add(tx.try_into().ok()?)?
74 } else {
75 tx.try_into().ok()?
76 },
77 ))
78 }
79}
80
81impl Display for RuneId {
82 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83 write!(f, "{}:{}", self.block, self.tx)
84 }
85}
86
87impl BorshSerialize for RuneId {
88 fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
89 borsh::BorshSerialize::serialize(&self.block, writer)?;
90 borsh::BorshSerialize::serialize(&self.tx, writer)?;
91 Ok(())
92 }
93}
94
95impl BorshDeserialize for RuneId {
96 fn deserialize(buf: &mut &[u8]) -> std::io::Result<Self> {
97 let block = <u64 as borsh::BorshDeserialize>::deserialize(buf)?;
98 let tx = <u32 as borsh::BorshDeserialize>::deserialize(buf)?;
99 Ok(RuneId::new(block, tx))
100 }
101
102 fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
103 let block = u64::deserialize_reader(reader)?;
104 let tx = u32::deserialize_reader(reader)?;
105 Ok(RuneId::new(block, tx))
106 }
107}
108
109impl FromStr for RuneId {
110 type Err = String;
111
112 fn from_str(s: &str) -> Result<Self, Self::Err> {
113 let (height, index) = s
114 .split_once(':')
115 .ok_or_else(|| "Invalid format: expected 'block:tx'".to_string())?;
116
117 let block = height
118 .parse::<u64>()
119 .map_err(|_| "Invalid block number".to_string())?;
120 let tx = index
121 .parse::<u32>()
122 .map_err(|_| "Invalid transaction number".to_string())?;
123
124 Ok(RuneId::new(block, tx))
125 }
126}
127
128impl Serialize for RuneId {
129 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
130 where
131 S: serde::Serializer,
132 {
133 serializer.serialize_str(&self.to_string())
134 }
135}
136
137impl<'de> Deserialize<'de> for RuneId {
138 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
139 where
140 D: serde::Deserializer<'de>,
141 {
142 let s = <String as serde::Deserialize>::deserialize(deserializer)?;
143 let rune_id = RuneId::from_str(&s).map_err(serde::de::Error::custom)?;
144 Ok(rune_id)
145 }
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151
152 #[test]
153 fn delta() {
154 let mut expected = [
155 RuneId::new(3, 1),
156 RuneId::new(4, 2),
157 RuneId::new(1, 2),
158 RuneId::new(1, 1),
159 RuneId::new(3, 1),
160 RuneId::new(2, 0),
161 ];
162
163 expected.sort();
164
165 assert_eq!(
166 expected,
167 [
168 RuneId::new(1, 1),
169 RuneId::new(1, 2),
170 RuneId::new(2, 0),
171 RuneId::new(3, 1),
172 RuneId::new(3, 1),
173 RuneId::new(4, 2),
174 ]
175 );
176
177 let mut previous = RuneId::default();
178 let mut deltas = Vec::new();
179 for id in expected {
180 deltas.push(previous.delta(id).unwrap());
181 previous = id;
182 }
183
184 assert_eq!(deltas, [(1, 1), (0, 1), (1, 0), (1, 1), (0, 0), (1, 2)]);
185
186 let mut previous = RuneId::default();
187 let mut actual = Vec::new();
188 for (block, tx) in deltas {
189 let next = previous.next(block, tx).unwrap();
190 actual.push(next);
191 previous = next;
192 }
193
194 assert_eq!(actual, expected);
195 }
196
197 #[test]
198 fn display() {
199 assert_eq!(RuneId::new(1, 2).to_string(), "1:2");
200 }
201
202 #[test]
203 fn from_str() {
204 assert!("123".parse::<RuneId>().is_err());
205 assert!(":".parse::<RuneId>().is_err());
206 assert!("1:".parse::<RuneId>().is_err());
207 assert!(":2".parse::<RuneId>().is_err());
208 assert!("a:2".parse::<RuneId>().is_err());
209 assert!("1:a".parse::<RuneId>().is_err());
210 assert_eq!("1:2".parse::<RuneId>().unwrap(), RuneId::new(1, 2));
211 assert_eq!("0:1".parse::<RuneId>().unwrap(), RuneId::new(0, 1));
213 }
214
215 #[test]
216 fn serde() {
217 let rune_id = RuneId::new(1, 2);
218 let json = "\"1:2\"";
219 assert_eq!(serde_json::to_string(&rune_id).unwrap(), json);
220 assert_eq!(serde_json::from_str::<RuneId>(json).unwrap(), rune_id);
221 }
222
223 #[test]
224 fn to_bytes() {
225 let rune_id = RuneId::new(0x1234567890ABCDEF, 0x12345678);
226 let bytes = rune_id.to_bytes();
227
228 assert_eq!(bytes[0..8], 0x1234567890ABCDEFu64.to_le_bytes());
230 assert_eq!(bytes[8..12], 0x12345678u32.to_le_bytes());
231 }
232
233 #[test]
234 fn get_sorted_rune_ids() {
235 let rune1 = RuneId::new(1, 1);
236 let rune2 = RuneId::new(2, 2);
237
238 let (a, b) = RuneId::get_sorted_rune_ids(&rune1, &rune2);
239 assert!(a <= b);
240
241 let (c, d) = RuneId::get_sorted_rune_ids(&rune2, &rune1);
243 assert_eq!((a, b), (c, d));
244 }
245
246 #[test]
247 fn borsh_roundtrip() {
248 let rune_id = RuneId::new(840000, 1);
249 let serialized = borsh::to_vec(&rune_id).unwrap();
250 let deserialized: RuneId = borsh::from_slice(&serialized).unwrap();
251 assert_eq!(rune_id, deserialized);
252 }
253}