blob: 388a2d0281c79557ebeaac44f00e418312565329 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use crate::util::HASH_SIZE;
use failure::Fail;
use hex::{FromHex, FromHexError, ToHex};
use std::fmt;
use std::str;
/// A SHA-256 hash.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Hash([u8; HASH_SIZE]);
impl Hash {
/// Obtain a slice of the bytes representing the hash.
pub fn as_bytes(&self) -> &[u8] {
&self.0[..]
}
}
impl str::FromStr for Hash {
type Err = ParseHashError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(FromHex::from_hex(s)?))
}
}
impl From<[u8; HASH_SIZE]> for Hash {
fn from(bytes: [u8; HASH_SIZE]) -> Self {
Hash(bytes)
}
}
impl fmt::Display for Hash {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.write_hex(f)
}
}
impl fmt::Debug for Hash {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("Hash").field(&self.to_string()).finish()
}
}
/// An error encountered while parsing a [`Hash`].
#[derive(Copy, Clone, Debug, Fail, PartialEq)]
pub struct ParseHashError(#[cause] FromHexError);
impl fmt::Display for ParseHashError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {
FromHexError::InvalidStringLength => {
write!(f, "{}, expected {} hex encoded bytes", self.0, HASH_SIZE)
}
_ => write!(f, "{}", self.0),
}
}
}
impl From<FromHexError> for ParseHashError {
fn from(e: FromHexError) -> Self {
ParseHashError(e)
}
}
#[cfg(test)]
mod tests {
use super::*;
use proptest::prelude::*;
use std::str::FromStr;
proptest! {
#[test]
fn test_from_str_display(ref s in "[[:xdigit:]]{64}") {
let hash = Hash::from_str(s).unwrap();
let display = format!("{}", hash);
prop_assert_eq!(s.to_ascii_lowercase(), display);
}
#[test]
fn test_rejects_odd_length_strings(ref s in "[[:xdigit:]][[:xdigit:]]{2}{0,128}") {
prop_assert_eq!(Err(FromHexError::OddLength.into()), Hash::from_str(s));
}
#[test]
fn test_rejects_incorrect_byte_count(ref s in "[[:xdigit:]]{2}{0,128}") {
prop_assume!(s.len() != HASH_SIZE * 2);
prop_assert_eq!(Err(FromHexError::InvalidStringLength.into()), Hash::from_str(s));
}
}
}