use std::{
fs,
path::{Path, PathBuf},
str::FromStr,
string::ToString,
};
use tectonic_errors::{Error, Result};
use thiserror::Error as ThisError;
pub use sha2::Digest;
pub use sha2::Sha256 as DigestComputer;
#[derive(ThisError, Debug)]
#[error("hexadecimal text had bad length: expected {expected}, observed {observed}")]
pub struct BadLengthError {
expected: usize,
observed: usize,
}
pub fn bytes_to_hex(bytes: &[u8]) -> String {
bytes
.iter()
.map(|b| format!("{b:02x}"))
.collect::<Vec<_>>()
.concat()
}
pub fn hex_to_bytes(text: &str, dest: &mut [u8]) -> Result<()> {
let n = dest.len();
let text_len = text.len();
if text_len != 2 * n {
return Err(BadLengthError {
expected: 2 * n,
observed: text_len,
}
.into());
}
for i in 0..n {
dest[i] = u8::from_str_radix(&text[i * 2..(i + 1) * 2], 16)?;
}
Ok(())
}
const N_BYTES: usize = 32;
pub const DIGEST_NAME: &str = "SHA256SUM";
pub const DIGEST_LEN: usize = 64;
pub fn create() -> DigestComputer {
Default::default()
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct DigestData([u8; N_BYTES]);
impl DigestData {
pub fn zeros() -> DigestData {
DigestData([0u8; N_BYTES])
}
pub fn of_nothing() -> DigestData {
let dc = create();
Self::from(dc)
}
pub fn create_two_part_path(&self, base: &Path) -> Result<PathBuf> {
let mut p = base.to_path_buf();
p.push(format!("{:02x}", self.0[0]));
fs::create_dir_all(&p)?;
p.push(bytes_to_hex(&self.0[1..]));
Ok(p)
}
}
impl ToString for DigestData {
fn to_string(&self) -> String {
bytes_to_hex(&self.0)
}
}
impl FromStr for DigestData {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
let mut result = DigestData::zeros();
hex_to_bytes(s, &mut result.0)?;
Ok(result)
}
}
impl From<DigestComputer> for DigestData {
fn from(s: DigestComputer) -> DigestData {
let mut result = DigestData::zeros();
let res = s.finalize();
result.0.copy_from_slice(res.as_slice());
result
}
}