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 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
//! The `Id` type and related functions
use std::{fmt, io::Read, ops::Deref, path::Path};
use binrw::{BinRead, BinWrite};
use derive_more::{Constructor, Display};
use rand::{thread_rng, RngCore};
use serde_derive::{Deserialize, Serialize};
use crate::{crypto::hasher::hash, error::IdErrorKind, RusticResult};
pub(super) mod constants {
/// The length of the hash in bytes
pub(super) const LEN: usize = 32;
/// The length of the hash in hexadecimal characters
pub(super) const HEX_LEN: usize = LEN * 2;
}
/// `Id` is the hash id of an object.
///
/// It is being used to identify blobs or files saved in the repository.
#[derive(
Serialize,
Deserialize,
Clone,
Copy,
Default,
PartialEq,
Eq,
Hash,
PartialOrd,
Ord,
Constructor,
BinWrite,
BinRead,
Display,
)]
#[display("{}", &self.to_hex()[0..8])]
pub struct Id(
/// The actual hash
#[serde(serialize_with = "hex::serde::serialize")]
#[serde(deserialize_with = "hex::serde::deserialize")]
[u8; constants::LEN],
);
impl Id {
/// Parse an `Id` from a hexadecimal string
///
/// # Arguments
///
/// * `s` - The hexadecimal string to parse
///
/// # Errors
///
/// * [`IdErrorKind::HexError`] - If the string is not a valid hexadecimal string
///
/// # Examples
///
/// ```
/// use rustic_core::Id;
///
/// let id = Id::from_hex("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef").unwrap();
///
/// assert_eq!(id.to_hex().as_str(), "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef");
/// ```
///
/// [`IdErrorKind::HexError`]: crate::error::IdErrorKind::HexError
pub fn from_hex(s: &str) -> RusticResult<Self> {
let mut id = Self::default();
hex::decode_to_slice(s, &mut id.0).map_err(IdErrorKind::HexError)?;
Ok(id)
}
/// Generate a random `Id`.
#[must_use]
pub fn random() -> Self {
let mut id = Self::default();
thread_rng().fill_bytes(&mut id.0);
id
}
/// Convert to [`HexId`].
///
/// # Examples
///
/// ```
/// use rustic_core::Id;
///
/// let id = Id::from_hex("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef").unwrap();
///
/// assert_eq!(id.to_hex().as_str(), "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef");
/// ```
///
/// # Panics
///
/// Panics if the `hex` crate fails to encode the hash
// TODO! - remove the panic
#[must_use]
pub fn to_hex(self) -> HexId {
let mut hex_id = HexId::EMPTY;
// HexId's len is LEN * 2
hex::encode_to_slice(self.0, &mut hex_id.0).unwrap();
hex_id
}
/// Checks if the [`Id`] is zero
///
/// # Examples
///
/// ```
/// use rustic_core::Id;
///
/// let id = Id::from_hex("0000000000000000000000000000000000000000000000000000000000000000").unwrap();
///
/// assert!(id.is_null());
/// ```
#[must_use]
pub fn is_null(&self) -> bool {
self == &Self::default()
}
/// Checks if this [`Id`] matches the content of a reader
///
/// # Arguments
///
/// * `length` - The length of the blob
/// * `r` - The reader to check
///
/// # Returns
///
/// `true` if the SHA256 matches, `false` otherwise
pub fn blob_matches_reader(&self, length: usize, r: &mut impl Read) -> bool {
// check if SHA256 matches
let mut vec = vec![0; length];
r.read_exact(&mut vec).is_ok() && self == &hash(&vec)
}
}
impl fmt::Debug for Id {
/// Format the `Id` as a hexadecimal string
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", &*self.to_hex())
}
}
/// An `Id` in hexadecimal format
#[derive(Copy, Clone, Debug)]
pub struct HexId([u8; constants::HEX_LEN]);
impl HexId {
/// An empty [`HexId`]
const EMPTY: Self = Self([b'0'; constants::HEX_LEN]);
/// Get the string representation of a [`HexId`]
///
/// # Panics
///
/// If the [`HexId`] is not a valid UTF-8 string
#[must_use]
pub fn as_str(&self) -> &str {
// This is only ever filled with hex chars, which are ascii
std::str::from_utf8(&self.0).unwrap()
}
}
impl Deref for HexId {
type Target = str;
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
impl AsRef<Path> for HexId {
fn as_ref(&self) -> &Path {
self.as_str().as_ref()
}
}