1use std::cmp::Ordering;
2
3use crate::{oid, ObjectId, Prefix};
4
5#[derive(Debug, thiserror::Error)]
7#[allow(missing_docs)]
8pub enum Error {
9 #[error(
10 "The minimum hex length of a short object id is {}, got {hex_len}",
11 Prefix::MIN_HEX_LEN
12 )]
13 TooShort { hex_len: usize },
14 #[error("An object of kind {object_kind} cannot be larger than {} in hex, but {hex_len} was requested", object_kind.len_in_hex())]
15 TooLong { object_kind: crate::Kind, hex_len: usize },
16}
17
18pub mod from_hex {
20 #[derive(Debug, Eq, PartialEq, thiserror::Error)]
22 #[allow(missing_docs)]
23 pub enum Error {
24 #[error(
25 "The minimum hex length of a short object id is {}, got {hex_len}",
26 super::Prefix::MIN_HEX_LEN
27 )]
28 TooShort { hex_len: usize },
29 #[error("An id cannot be larger than {} chars in hex, but {hex_len} was requested", crate::Kind::longest().len_in_hex())]
30 TooLong { hex_len: usize },
31 #[error("Invalid hex character")]
32 Invalid,
33 }
34}
35
36impl Prefix {
37 pub const MIN_HEX_LEN: usize = 4;
39
40 pub fn new(id: &oid, hex_len: usize) -> Result<Self, Error> {
45 if hex_len > id.kind().len_in_hex() {
46 Err(Error::TooLong {
47 object_kind: id.kind(),
48 hex_len,
49 })
50 } else if hex_len < Self::MIN_HEX_LEN {
51 Err(Error::TooShort { hex_len })
52 } else {
53 let mut prefix = ObjectId::null(id.kind());
54 let b = prefix.as_mut_slice();
55 let copy_len = hex_len.div_ceil(2);
56 b[..copy_len].copy_from_slice(&id.as_bytes()[..copy_len]);
57 if hex_len % 2 == 1 {
58 b[hex_len / 2] &= 0xf0;
59 }
60
61 Ok(Prefix { bytes: prefix, hex_len })
62 }
63 }
64
65 pub fn as_oid(&self) -> &oid {
70 &self.bytes
71 }
72
73 pub fn hex_len(&self) -> usize {
77 self.hex_len
78 }
79
80 pub fn cmp_oid(&self, candidate: &oid) -> Ordering {
83 let common_len = self.hex_len / 2;
84
85 self.bytes.as_bytes()[..common_len]
86 .cmp(&candidate.as_bytes()[..common_len])
87 .then(if self.hex_len % 2 == 1 {
88 let half_byte_idx = self.hex_len / 2;
89 self.bytes.as_bytes()[half_byte_idx].cmp(&(candidate.as_bytes()[half_byte_idx] & 0xf0))
90 } else {
91 Ordering::Equal
92 })
93 }
94
95 pub fn from_hex(value: &str) -> Result<Self, from_hex::Error> {
98 let hex_len = value.len();
99 if hex_len < Self::MIN_HEX_LEN {
100 return Err(from_hex::Error::TooShort { hex_len });
101 }
102 Self::from_hex_nonempty(value)
103 }
104
105 pub fn from_hex_nonempty(value: &str) -> Result<Self, from_hex::Error> {
108 let hex_len = value.len();
109
110 if hex_len > crate::Kind::longest().len_in_hex() {
111 return Err(from_hex::Error::TooLong { hex_len });
112 } else if hex_len == 0 {
113 return Err(from_hex::Error::TooShort { hex_len });
114 }
115
116 let src = if value.len() % 2 == 0 {
117 let mut out = Vec::from_iter(std::iter::repeat_n(0, value.len() / 2));
118 faster_hex::hex_decode(value.as_bytes(), &mut out).map(move |_| out)
119 } else {
120 let mut buf = [0u8; crate::Kind::longest().len_in_hex()];
122 buf[..value.len()].copy_from_slice(value.as_bytes());
123 buf[value.len()] = b'0';
124 let src = &buf[..=value.len()];
125 let mut out = Vec::from_iter(std::iter::repeat_n(0, src.len() / 2));
126 faster_hex::hex_decode(src, &mut out).map(move |_| out)
127 }
128 .map_err(|e| match e {
129 faster_hex::Error::InvalidChar | faster_hex::Error::Overflow => from_hex::Error::Invalid,
130 faster_hex::Error::InvalidLength(_) => panic!("This is already checked"),
131 })?;
132
133 let mut bytes = ObjectId::null(crate::Kind::from_hex_len(value.len()).expect("hex-len is already checked"));
134 let dst = bytes.as_mut_slice();
135 let copy_len = src.len();
136 dst[..copy_len].copy_from_slice(&src);
137
138 Ok(Prefix { bytes, hex_len })
139 }
140}
141
142impl TryFrom<&str> for Prefix {
145 type Error = from_hex::Error;
146
147 fn try_from(value: &str) -> Result<Self, Self::Error> {
148 Prefix::from_hex(value)
149 }
150}
151
152impl std::fmt::Display for Prefix {
153 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154 self.bytes.to_hex_with_len(self.hex_len).fmt(f)
155 }
156}
157
158impl From<ObjectId> for Prefix {
159 fn from(oid: ObjectId) -> Self {
160 Prefix {
161 bytes: oid,
162 hex_len: oid.kind().len_in_hex(),
163 }
164 }
165}