use serde::de::Error;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::str::FromStr;
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct Sha1([u8; 20]);
impl AsRef<[u8; 20]> for Sha1 {
fn as_ref(&self) -> &[u8; 20] {
&self.0
}
}
impl AsRef<[u8]> for Sha1 {
fn as_ref(&self) -> &[u8] {
self.0.as_slice()
}
}
impl PartialEq<&[u8]> for Sha1 {
fn eq(&self, other: &&[u8]) -> bool {
self.0.as_slice() == *other
}
}
impl PartialEq<Sha1> for &[u8] {
fn eq(&self, other: &Sha1) -> bool {
*self == other.0
}
}
impl PartialEq<[u8; 20]> for Sha1 {
fn eq(&self, other: &[u8; 20]) -> bool {
&self.0 == other
}
}
impl PartialEq<Sha1> for [u8; 20] {
fn eq(&self, other: &Sha1) -> bool {
self == &other.0
}
}
#[derive(thiserror::Error, Debug)]
pub enum Sha1ParseError {
#[error("SHA-1 digest must be 40 characters long: got {0}")]
Length(usize),
#[error("SHA-1 digest must hexadecimal: got {0}")]
Digits(String),
}
impl FromStr for Sha1 {
type Err = Sha1ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.trim();
if s.len() != 40 {
return Err(Sha1ParseError::Length(s.len()));
}
let mut bytes = [0u8; 20];
for octet in 0..20 {
bytes[octet] = u8::from_str_radix(&s[octet * 2..octet * 2 + 2], 16)
.map_err(|_| Sha1ParseError::Digits(s.into()))?;
}
Ok(Self(bytes))
}
}
impl std::fmt::Display for Sha1 {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f,
"{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
self.0[0],
self.0[1],
self.0[2],
self.0[3],
self.0[4],
self.0[5],
self.0[6],
self.0[7],
self.0[8],
self.0[9],
self.0[10],
self.0[11],
self.0[12],
self.0[13],
self.0[14],
self.0[15],
self.0[16],
self.0[17],
self.0[18],
self.0[19],
)
}
}
impl From<[u8; 20]> for Sha1 {
fn from(v: [u8; 20]) -> Self {
Self(v)
}
}
impl From<Sha1> for [u8; 20] {
fn from(v: Sha1) -> Self {
v.0
}
}
impl Serialize for Sha1 {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl<'de> Deserialize<'de> for Sha1 {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let str = <std::borrow::Cow<str>>::deserialize(deserializer)?;
str.parse().map_err(D::Error::custom)
}
}