1use crate::CommitHashParseError;
6use std::{fmt, str::FromStr};
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
26pub enum GitCommitHash {
27 Sha1([u8; 20]),
29 Sha256([u8; 32]),
31}
32
33impl FromStr for GitCommitHash {
34 type Err = CommitHashParseError;
35
36 fn from_str(s: &str) -> Result<Self, Self::Err> {
37 let len = s.len();
38 match len {
39 40 => {
40 let mut bytes = [0; 20];
41 hex::decode_to_slice(s, &mut bytes)
42 .map_err(CommitHashParseError::InvalidHex)?;
43 Ok(GitCommitHash::Sha1(bytes))
44 }
45 64 => {
46 let mut bytes = [0; 32];
47 hex::decode_to_slice(s, &mut bytes)
48 .map_err(CommitHashParseError::InvalidHex)?;
49 Ok(GitCommitHash::Sha256(bytes))
50 }
51 _ => Err(CommitHashParseError::InvalidLength(len)),
52 }
53 }
54}
55
56impl fmt::Display for GitCommitHash {
57 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58 match self {
59 GitCommitHash::Sha1(bytes) => hex::encode(bytes).fmt(f),
60 GitCommitHash::Sha256(bytes) => hex::encode(bytes).fmt(f),
61 }
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68
69 const VALID_SHA1: &str = "0123456789abcdef0123456789abcdef01234567";
70 const VALID_SHA256: &str =
71 "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
72
73 #[test]
74 fn test_commit_hash_valid() {
75 let hash: GitCommitHash = VALID_SHA1.parse().unwrap();
76 assert_eq!(hash.to_string(), VALID_SHA1);
77
78 let hash: GitCommitHash = VALID_SHA256.parse().unwrap();
79 assert_eq!(hash.to_string(), VALID_SHA256);
80 }
81
82 #[test]
83 fn test_commit_hash_invalid() {
84 assert_eq!(
85 "abc123".parse::<GitCommitHash>(),
86 Err(CommitHashParseError::InvalidLength(6)),
87 "too short"
88 );
89
90 assert_eq!(
91 VALID_SHA1[..39].parse::<GitCommitHash>(),
92 Err(CommitHashParseError::InvalidLength(39)),
93 "39 chars (one short of SHA-1)"
94 );
95
96 let input = format!("{}0", VALID_SHA1);
97 assert_eq!(
98 input.parse::<GitCommitHash>(),
99 Err(CommitHashParseError::InvalidLength(41)),
100 "41 chars (one over SHA-1)"
101 );
102
103 assert!(
104 matches!(
105 "0123456789abcdefg123456789abcdef01234567"
106 .parse::<GitCommitHash>(),
107 Err(CommitHashParseError::InvalidHex(_))
108 ),
109 "non-hex character 'g'"
110 );
111
112 let input = format!(" {}", VALID_SHA1);
113 assert_eq!(
114 input.parse::<GitCommitHash>(),
115 Err(CommitHashParseError::InvalidLength(41)),
116 "leading whitespace (the CommitHash parser doesn't do trimming)"
117 );
118 }
119
120 #[test]
121 fn test_commit_hash_empty_string() {
122 assert_eq!(
123 "".parse::<GitCommitHash>(),
124 Err(CommitHashParseError::InvalidLength(0)),
125 "empty string"
126 );
127 }
128}