1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use std::fmt;
use std::cmp::Ordering;
use std::hash::{Hasher, Hash};
use std::str;
use libc;
use {raw, Error};
use util::Binding;
#[derive(Copy)]
pub struct Oid {
raw: raw::git_oid,
}
impl Oid {
pub fn from_str(s: &str) -> Result<Oid, Error> {
::init();
let mut raw = raw::git_oid { id: [0; raw::GIT_OID_RAWSZ] };
unsafe {
try_call!(raw::git_oid_fromstrn(&mut raw,
s.as_bytes().as_ptr()
as *const libc::c_char,
s.len() as libc::size_t));
}
Ok(Oid { raw: raw })
}
pub fn from_bytes(bytes: &[u8]) -> Result<Oid, Error> {
::init();
let mut raw = raw::git_oid { id: [0; raw::GIT_OID_RAWSZ] };
if bytes.len() != raw::GIT_OID_RAWSZ {
Err(Error::from_str("raw byte array must be 20 bytes"))
} else {
unsafe { raw::git_oid_fromraw(&mut raw, bytes.as_ptr()) }
Ok(Oid { raw: raw })
}
}
pub fn as_bytes(&self) -> &[u8] { &self.raw.id }
pub fn is_zero(&self) -> bool {
unsafe { raw::git_oid_iszero(&self.raw) == 1 }
}
}
impl Binding for Oid {
type Raw = *const raw::git_oid;
unsafe fn from_raw(oid: *const raw::git_oid) -> Oid {
Oid { raw: *oid }
}
fn raw(&self) -> *const raw::git_oid { &self.raw as *const _ }
}
impl fmt::Debug for Oid {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl fmt::Display for Oid {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut dst = [0u8; raw::GIT_OID_HEXSZ + 1];
unsafe {
raw::git_oid_tostr(dst.as_mut_ptr() as *mut libc::c_char,
dst.len() as libc::size_t, &self.raw);
}
let s = &dst[..dst.iter().position(|&a| a == 0).unwrap()];
str::from_utf8(s).unwrap().fmt(f)
}
}
impl PartialEq for Oid {
fn eq(&self, other: &Oid) -> bool {
unsafe { raw::git_oid_equal(&self.raw, &other.raw) != 0 }
}
}
impl Eq for Oid {}
impl PartialOrd for Oid {
fn partial_cmp(&self, other: &Oid) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Oid {
fn cmp(&self, other: &Oid) -> Ordering {
match unsafe { raw::git_oid_cmp(&self.raw, &other.raw) } {
0 => Ordering::Equal,
n if n < 0 => Ordering::Less,
_ => Ordering::Greater,
}
}
}
impl Clone for Oid {
fn clone(&self) -> Oid { *self }
}
impl Hash for Oid {
fn hash<H: Hasher>(&self, into: &mut H) {
self.raw.id.hash(into)
}
}
#[cfg(test)]
mod tests {
use super::Oid;
#[test]
fn conversions() {
assert!(Oid::from_str("foo").is_err());
assert!(Oid::from_str("decbf2be529ab6557d5429922251e5ee36519817").is_ok());
assert!(Oid::from_bytes(b"foo").is_err());
assert!(Oid::from_bytes(b"00000000000000000000").is_ok());
}
}