github_types/
oid.rs

1// Copyright (c) 2019 Jason White
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20use std::fmt;
21use std::ops;
22
23use hex::{FromHex, FromHexError, ToHex};
24use serde::de::{self, Deserialize, Deserializer, Visitor};
25use serde::ser::{self, Serialize, Serializer};
26
27/// A Git object ID (i.e., a SHA1).
28#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Default)]
29pub struct Oid([u8; 20]);
30
31impl Oid {
32    pub fn from_hex(s: &str) -> Result<Self, ()> {
33        Ok(Oid(<[u8; 20]>::from_hex(s).map_err(|_| ())?))
34    }
35
36    /// The empty tree sha `4b825dc642cb6eb9a060e54bf8d69288fbee4904`.
37    ///
38    /// This can be computed manually with `git hash-object -t tree /dev/null`.
39    pub fn empty_tree() -> Oid {
40        Oid([
41            0x4b, 0x82, 0x5d, 0xc6, 0x42, 0xcb, 0x6e, 0xb9, 0xa0, 0x60, 0xe5,
42            0x4b, 0xf8, 0xd6, 0x92, 0x88, 0xfb, 0xee, 0x49, 0x04,
43        ])
44    }
45
46    /// A sha of all zeros.
47    pub fn zero() -> Oid {
48        Oid([
49            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
50            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
51        ])
52    }
53}
54
55impl fmt::UpperHex for Oid {
56    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
57        self.write_hex_upper(f)
58    }
59}
60
61impl fmt::LowerHex for Oid {
62    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
63        self.write_hex(f)
64    }
65}
66
67impl fmt::Display for Oid {
68    #[inline]
69    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
70        <Self as fmt::LowerHex>::fmt(self, f)
71    }
72}
73
74impl fmt::Debug for Oid {
75    #[inline]
76    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
77        <Self as fmt::Display>::fmt(self, f)
78    }
79}
80
81impl ops::Deref for Oid {
82    type Target = [u8; 20];
83
84    fn deref(&self) -> &Self::Target {
85        &self.0
86    }
87}
88
89impl Serialize for Oid {
90    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
91    where
92        S: Serializer,
93    {
94        if serializer.is_human_readable() {
95            // Serialize as a hex string.
96            let mut hex = String::new();
97            self.0
98                .as_ref()
99                .write_hex(&mut hex)
100                .map_err(ser::Error::custom)?;
101            serializer.serialize_str(&hex)
102        } else {
103            // Serialize as a byte array with known length.
104            serializer.serialize_bytes(self.0.as_ref())
105        }
106    }
107}
108
109impl<'de> Deserialize<'de> for Oid {
110    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
111    where
112        D: Deserializer<'de>,
113    {
114        struct OidVisitor;
115
116        impl<'de> Visitor<'de> for OidVisitor {
117            type Value = Oid;
118
119            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
120                write!(f, "hex string or 20 bytes")
121            }
122
123            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
124            where
125                E: de::Error,
126            {
127                let v = <[u8; 20]>::from_hex(v).map_err(|e| match e {
128                    FromHexError::InvalidHexCharacter { c, .. } => {
129                        E::invalid_value(
130                            de::Unexpected::Char(c),
131                            &"string with only hexadecimal characters",
132                        )
133                    }
134                    FromHexError::InvalidStringLength => E::invalid_length(
135                        v.len(),
136                        &"hex string with a valid length",
137                    ),
138                    FromHexError::OddLength => E::invalid_length(
139                        v.len(),
140                        &"hex string with an even length",
141                    ),
142                })?;
143
144                Ok(Oid(v))
145            }
146
147            fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
148            where
149                E: de::Error,
150            {
151                if v.len() != 20 {
152                    return Err(E::invalid_length(v.len(), &"20 bytes"));
153                }
154
155                let mut inner = <[u8; 20]>::default();
156                inner.copy_from_slice(v);
157
158                Ok(Oid(inner))
159            }
160        }
161
162        if deserializer.is_human_readable() {
163            deserializer.deserialize_str(OidVisitor)
164        } else {
165            deserializer.deserialize_bytes(OidVisitor)
166        }
167    }
168}