1use std::fmt;
19use std::fmt::Debug;
20
21use crate::hex_util;
22
23pub trait ObjectId {
26 fn object_type(&self) -> String;
28 fn as_bytes(&self) -> &[u8];
30 fn to_bytes(&self) -> Vec<u8>;
32 fn hex(&self) -> String;
34}
35
36#[doc(hidden)] #[macro_export]
55macro_rules! _id_type {
56 ( $(#[$attr:meta])*
57 $vis:vis $name:ident { $hex_method:ident() }
58 ) => {
59 $(#[$attr])*
60 #[derive($crate::content_hash::ContentHash, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
61 $vis struct $name(Vec<u8>);
62 $crate::object_id::_impl_id_type!($name, $hex_method);
63 };
64}
65
66#[doc(hidden)]
67#[macro_export]
68macro_rules! _impl_id_type {
69 ($name:ident, $hex_method:ident) => {
70 #[allow(dead_code)]
71 impl $name {
72 pub fn new(value: Vec<u8>) -> Self {
74 Self(value)
75 }
76
77 pub fn from_bytes(bytes: &[u8]) -> Self {
79 Self(bytes.to_vec())
80 }
81
82 pub fn from_hex(hex: &'static str) -> Self {
87 Self::try_from_hex(hex).unwrap()
88 }
89
90 pub fn try_from_hex(hex: impl AsRef<[u8]>) -> Option<Self> {
92 $crate::hex_util::decode_hex(hex).map(Self)
93 }
94 }
95
96 impl std::fmt::Debug for $name {
97 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
98 f.debug_tuple(stringify!($name)).field(&self.hex()).finish()
100 }
101 }
102
103 impl std::fmt::Display for $name {
104 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
105 f.pad(&self.$hex_method())
106 }
107 }
108
109 impl serde::Serialize for $name {
110 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
111 where
112 S: serde::Serializer,
113 {
114 if serializer.is_human_readable() {
115 self.$hex_method().serialize(serializer)
116 } else {
117 self.as_bytes().serialize(serializer)
118 }
119 }
120 }
121
122 impl $crate::object_id::ObjectId for $name {
123 fn object_type(&self) -> String {
124 stringify!($name)
125 .strip_suffix("Id")
126 .unwrap()
127 .to_ascii_lowercase()
128 .to_string()
129 }
130
131 fn as_bytes(&self) -> &[u8] {
132 &self.0
133 }
134
135 fn to_bytes(&self) -> Vec<u8> {
136 self.0.clone()
137 }
138
139 fn hex(&self) -> String {
140 $crate::hex_util::encode_hex(&self.0)
141 }
142 }
143 };
144}
145
146#[doc(inline)]
147pub use _id_type as id_type;
148pub use _impl_id_type;
149
150#[derive(Clone, PartialEq, Eq)]
153pub struct HexPrefix {
154 min_prefix_bytes: Vec<u8>,
157 has_odd_byte: bool,
158}
159
160impl HexPrefix {
161 pub fn try_from_hex(prefix: impl AsRef<[u8]>) -> Option<Self> {
164 let (min_prefix_bytes, has_odd_byte) = hex_util::decode_hex_prefix(prefix)?;
165 Some(Self {
166 min_prefix_bytes,
167 has_odd_byte,
168 })
169 }
170
171 pub fn try_from_reverse_hex(prefix: impl AsRef<[u8]>) -> Option<Self> {
174 let (min_prefix_bytes, has_odd_byte) = hex_util::decode_reverse_hex_prefix(prefix)?;
175 Some(Self {
176 min_prefix_bytes,
177 has_odd_byte,
178 })
179 }
180
181 pub fn from_bytes(bytes: &[u8]) -> Self {
184 Self {
185 min_prefix_bytes: bytes.to_owned(),
186 has_odd_byte: false,
187 }
188 }
189
190 pub fn from_id<T: ObjectId + ?Sized>(id: &T) -> Self {
192 Self::from_bytes(id.as_bytes())
193 }
194
195 pub fn hex(&self) -> String {
197 let mut hex_string = hex_util::encode_hex(&self.min_prefix_bytes);
198 if self.has_odd_byte {
199 hex_string.pop().unwrap();
200 }
201 hex_string
202 }
203
204 pub fn reverse_hex(&self) -> String {
206 let mut hex_string = hex_util::encode_reverse_hex(&self.min_prefix_bytes);
207 if self.has_odd_byte {
208 hex_string.pop().unwrap();
209 }
210 hex_string
211 }
212
213 pub fn min_prefix_bytes(&self) -> &[u8] {
217 &self.min_prefix_bytes
218 }
219
220 pub fn as_full_bytes(&self) -> Option<&[u8]> {
222 (!self.has_odd_byte).then_some(&self.min_prefix_bytes)
223 }
224
225 fn split_odd_byte(&self) -> (Option<u8>, &[u8]) {
226 if self.has_odd_byte {
227 let (&odd, prefix) = self.min_prefix_bytes.split_last().unwrap();
228 (Some(odd), prefix)
229 } else {
230 (None, &self.min_prefix_bytes)
231 }
232 }
233
234 pub fn matches<Q: ObjectId>(&self, id: &Q) -> bool {
236 let id_bytes = id.as_bytes();
237 let (maybe_odd, prefix) = self.split_odd_byte();
238 if id_bytes.starts_with(prefix) {
239 if let Some(odd) = maybe_odd {
240 matches!(id_bytes.get(prefix.len()), Some(v) if v & 0xf0 == odd)
241 } else {
242 true
243 }
244 } else {
245 false
246 }
247 }
248}
249
250impl Debug for HexPrefix {
251 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
252 f.debug_tuple("HexPrefix").field(&self.hex()).finish()
253 }
254}
255
256#[derive(Debug, Clone, PartialEq, Eq)]
258pub enum PrefixResolution<T> {
259 NoMatch,
261 SingleMatch(T),
263 AmbiguousMatch,
265}
266
267impl<T> PrefixResolution<T> {
268 pub fn map<U>(self, f: impl FnOnce(T) -> U) -> PrefixResolution<U> {
270 match self {
271 Self::NoMatch => PrefixResolution::NoMatch,
272 Self::SingleMatch(x) => PrefixResolution::SingleMatch(f(x)),
273 Self::AmbiguousMatch => PrefixResolution::AmbiguousMatch,
274 }
275 }
276
277 pub fn filter_map<U>(self, f: impl FnOnce(T) -> Option<U>) -> PrefixResolution<U> {
280 match self {
281 Self::NoMatch => PrefixResolution::NoMatch,
282 Self::SingleMatch(x) => match f(x) {
283 None => PrefixResolution::NoMatch,
284 Some(y) => PrefixResolution::SingleMatch(y),
285 },
286 Self::AmbiguousMatch => PrefixResolution::AmbiguousMatch,
287 }
288 }
289}
290
291impl<T: Clone> PrefixResolution<T> {
292 pub fn plus(&self, other: &Self) -> Self {
295 match (self, other) {
296 (Self::NoMatch, other) => other.clone(),
297 (local, Self::NoMatch) => local.clone(),
298 (Self::AmbiguousMatch, _) => Self::AmbiguousMatch,
299 (_, Self::AmbiguousMatch) => Self::AmbiguousMatch,
300 (Self::SingleMatch(_), Self::SingleMatch(_)) => Self::AmbiguousMatch,
301 }
302 }
303}
304
305#[cfg(test)]
306mod tests {
307 use super::*;
308
309 id_type!(SomeId { hex() });
310
311 #[test]
312 fn test_hex_prefix_prefixes() {
313 let prefix = HexPrefix::try_from_hex("").unwrap();
314 assert_eq!(prefix.min_prefix_bytes(), b"");
315
316 let prefix = HexPrefix::try_from_hex("1").unwrap();
317 assert_eq!(prefix.min_prefix_bytes(), b"\x10");
318
319 let prefix = HexPrefix::try_from_hex("12").unwrap();
320 assert_eq!(prefix.min_prefix_bytes(), b"\x12");
321
322 let prefix = HexPrefix::try_from_hex("123").unwrap();
323 assert_eq!(prefix.min_prefix_bytes(), b"\x12\x30");
324
325 let bad_prefix = HexPrefix::try_from_hex("0x123");
326 assert_eq!(bad_prefix, None);
327
328 let bad_prefix = HexPrefix::try_from_hex("foobar");
329 assert_eq!(bad_prefix, None);
330 }
331
332 #[test]
333 fn test_hex_prefix_matches() {
334 let id = SomeId::from_hex("1234");
335
336 assert!(HexPrefix::try_from_hex("").unwrap().matches(&id));
337 assert!(HexPrefix::try_from_hex("1").unwrap().matches(&id));
338 assert!(HexPrefix::try_from_hex("12").unwrap().matches(&id));
339 assert!(HexPrefix::try_from_hex("123").unwrap().matches(&id));
340 assert!(HexPrefix::try_from_hex("1234").unwrap().matches(&id));
341 assert!(!HexPrefix::try_from_hex("12345").unwrap().matches(&id));
342
343 assert!(!HexPrefix::try_from_hex("a").unwrap().matches(&id));
344 assert!(!HexPrefix::try_from_hex("1a").unwrap().matches(&id));
345 assert!(!HexPrefix::try_from_hex("12a").unwrap().matches(&id));
346 assert!(!HexPrefix::try_from_hex("123a").unwrap().matches(&id));
347 }
348}