1use std::{borrow::Borrow, ops::Deref, str::FromStr};
2
3use crate::{ChangeId, Kind, ObjectId, oid};
4
5const REVERSE_HEX: &[u8; 16] = b"zyxwvutsrqponmlk";
6
7impl ChangeId {
8 pub fn from_reverse_hex(buffer: &[u8]) -> Result<Self, crate::decode::Error> {
10 let len = buffer.len();
11 if crate::Kind::from_hex_len(len).is_none_or(|kind| kind.len_in_hex() != len) {
12 return Err(crate::decode::Error::InvalidHexEncodingLength(len));
13 }
14
15 let mut hex = Kind::hex_buf();
16 reverse_hex_to_hex(buffer, &mut hex[..len]).map_err(|()| crate::decode::Error::Invalid)?;
17 ObjectId::from_hex(&hex[..len]).map(ChangeId)
18 }
19
20 pub fn to_reverse_hex(&self) -> ReverseHexDisplay<'_> {
22 ReverseHexDisplay::new(self, self.kind().len_in_hex())
23 }
24
25 pub fn to_reverse_hex_with_len(&self, len: usize) -> ReverseHexDisplay<'_> {
27 ReverseHexDisplay::new(self, len)
28 }
29}
30
31#[derive(PartialEq, Eq, Hash, Ord, PartialOrd)]
33pub struct ReverseHexDisplay<'a> {
34 inner: &'a oid,
35 hex_len: usize,
36}
37
38impl FromStr for ChangeId {
39 type Err = crate::decode::Error;
40
41 fn from_str(value: &str) -> Result<Self, Self::Err> {
42 Self::from_reverse_hex(value.as_bytes())
43 }
44}
45
46impl From<ObjectId> for ChangeId {
47 fn from(value: ObjectId) -> Self {
48 ChangeId(value)
49 }
50}
51
52impl From<&oid> for ChangeId {
53 fn from(value: &oid) -> Self {
54 ChangeId(value.into())
55 }
56}
57
58impl From<ChangeId> for ObjectId {
59 fn from(value: ChangeId) -> Self {
60 value.0
61 }
62}
63
64impl Deref for ChangeId {
65 type Target = oid;
66
67 fn deref(&self) -> &Self::Target {
68 self.0.as_ref()
69 }
70}
71
72impl AsRef<oid> for ChangeId {
73 fn as_ref(&self) -> &oid {
74 self
75 }
76}
77
78impl Borrow<oid> for ChangeId {
79 fn borrow(&self) -> &oid {
80 self
81 }
82}
83
84impl std::fmt::Display for ChangeId {
85 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86 self.to_reverse_hex().fmt(f)
87 }
88}
89
90impl ChangeId {
91 fn eq_str(&self, other: &str) -> bool {
92 self.to_reverse_hex().eq_str(other)
93 }
94}
95
96impl_partial_eq_str!(ChangeId);
97
98impl ReverseHexDisplay<'_> {
99 pub(crate) fn new(inner: &oid, hex_len: usize) -> ReverseHexDisplay<'_> {
100 ReverseHexDisplay { inner, hex_len }
101 }
102
103 fn eq_str(&self, other: &str) -> bool {
104 let mut buf = Kind::hex_buf();
105 let reverse_hex = encode_reverse_hex(self.inner, &mut buf);
106 reverse_hex[..self.hex_len.min(reverse_hex.len())] == *other
107 }
108}
109
110impl_partial_eq_str_one_way!(ReverseHexDisplay<'_>);
112
113impl std::fmt::Display for ReverseHexDisplay<'_> {
114 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
115 let mut buf = Kind::hex_buf();
116 let reverse_hex = encode_reverse_hex(self.inner, &mut buf);
117 f.write_str(&reverse_hex[..self.hex_len.min(reverse_hex.len())])
118 }
119}
120
121pub(crate) fn reverse_hex_to_hex(reverse_hex: &[u8], hex: &mut [u8]) -> Result<(), ()> {
122 for (src, dst) in reverse_hex.iter().zip(hex) {
123 let nibble = match src {
124 b'k'..=b'z' => b'z' - src,
125 b'K'..=b'Z' => b'Z' - src,
126 _ => return Err(()),
127 };
128 *dst = b"0123456789abcdef"[usize::from(nibble)];
129 }
130 Ok(())
131}
132
133fn encode_reverse_hex<'a>(id: &oid, buf: &'a mut [u8]) -> &'a str {
134 for (byte, pair) in id.as_bytes().iter().zip(buf.chunks_exact_mut(2)) {
135 pair[0] = REVERSE_HEX[usize::from(byte >> 4)];
136 pair[1] = REVERSE_HEX[usize::from(byte & 0x0f)];
137 }
138 let len = id.kind().len_in_hex();
139 std::str::from_utf8(&buf[..len]).expect("reverse hex is always ASCII")
140}