rustup_toolchain_manifest/
hash_value.rs

1use serde::{Deserialize, Deserializer, Serialize, Serializer};
2use std::str::FromStr;
3use thiserror::Error;
4
5/// Parse errors for `HashValue`
6#[derive(Clone, Copy, Debug, Error)]
7pub enum ParseError {
8    /// An input character was invalid (not-hexadecimal)
9    #[error("Invalid byte: {0}")]
10    InvalidByte(u8),
11
12    /// The hash was not a mutiple of 8-bits (had an odd number of characters)
13    #[error("Not a multiple of 8 bits")]
14    NotOctetSized,
15}
16
17/// A dynamically sized hash used to represent Git SHAs and digest values
18#[derive(Default, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
19pub struct HashValue {
20    bytes: Vec<u8>,
21}
22
23impl HashValue {
24    fn nibble_to_ascii(c: u8) -> u8 {
25        match c {
26            0..=9 => b'0' + c,
27            10..=15 => b'a' + (c - 10),
28            _ => panic!("Value not in range 0-15: {}", c),
29        }
30    }
31
32    fn ascii_to_nibble(c: u8) -> Result<u8, ParseError> {
33        match c {
34            b'0'..=b'9' => Ok(c - b'0'),
35            b'a'..=b'f' => Ok(c - b'a' + 10),
36            b'A'..=b'F' => Ok(c - b'A' + 10),
37            _ => Err(ParseError::InvalidByte(c)),
38        }
39    }
40
41    /// Returns the value as ASCII characters
42    fn to_ascii(&self) -> Vec<u8> {
43        let mut characters = vec![0u8; self.bytes.len() * 2];
44        for (idx, character) in self.bytes.iter().enumerate() {
45            let high = character >> 4;
46            let low = character & 0xf;
47            characters[idx * 2] = Self::nibble_to_ascii(high);
48            characters[idx * 2 + 1] = Self::nibble_to_ascii(low);
49        }
50        characters
51    }
52
53    /// Converts from binary (not ASCII)
54    #[must_use]
55    pub fn from_bytes(bytes: &[u8]) -> HashValue {
56        HashValue { bytes: bytes.to_vec() }
57    }
58}
59
60impl AsRef<[u8]> for HashValue {
61    fn as_ref(&self) -> &[u8] {
62        self.bytes.as_ref()
63    }
64}
65
66impl std::fmt::Debug for HashValue {
67    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
68        write!(formatter, "\"{}\"", self)
69    }
70}
71
72impl std::fmt::Display for HashValue {
73    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
74        let ascii = self.to_ascii();
75        let ascii = unsafe { std::str::from_utf8_unchecked(&ascii) };
76        formatter.write_str(ascii)
77    }
78}
79
80impl Serialize for HashValue {
81    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
82    where
83        S: Serializer,
84    {
85        if serializer.is_human_readable() {
86            let ascii = self.to_ascii();
87            let ascii = unsafe { std::str::from_utf8_unchecked(&ascii) };
88            serializer.serialize_str(ascii)
89        } else {
90            serializer.serialize_bytes(&self.bytes)
91        }
92    }
93}
94
95impl<'a> Deserialize<'a> for HashValue {
96    fn deserialize<D>(deserializer: D) -> Result<HashValue, D::Error>
97    where
98        D: Deserializer<'a>,
99    {
100        if deserializer.is_human_readable() {
101            let s = String::deserialize(deserializer)?;
102            Ok(HashValue::from_str(s.as_str()).map_err(serde::de::Error::custom)?)
103        } else {
104            let bytes = <Vec<u8>>::deserialize(deserializer)?;
105            Ok(HashValue { bytes })
106        }
107    }
108}
109
110impl std::str::FromStr for HashValue {
111    type Err = ParseError;
112
113    fn from_str(string: &str) -> Result<HashValue, ParseError> {
114        let string = string.as_bytes();
115        let length = string.len();
116        if length % 2 == 0 {
117            let mut bytes = vec![0u8; length / 2];
118            for (idx, byte) in bytes.iter_mut().enumerate() {
119                let high = Self::ascii_to_nibble(string[idx * 2])?;
120                let low = Self::ascii_to_nibble(string[idx * 2 + 1])?;
121                *byte = (high << 4) | low;
122            }
123            Ok(HashValue { bytes })
124        } else {
125            Err(ParseError::NotOctetSized)
126        }
127    }
128}