mirror of
https://github.com/Yuyi-Oak/BlueArchiveToolkit.git
synced 2026-07-22 09:15:15 +08:00
chore: establish development baseline
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
//! Hash 计算模块
|
||||
//!
|
||||
//! 使用 BLAKE3 算法计算内容 Hash
|
||||
|
||||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
|
||||
/// Hash 类型
|
||||
///
|
||||
/// 使用 BLAKE3 算法计算的 256 位 Hash
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct Hash([u8; 32]);
|
||||
|
||||
impl Hash {
|
||||
/// 从字节数组创建 Hash
|
||||
pub fn from_bytes(bytes: [u8; 32]) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
|
||||
/// 获取 Hash 的字节表示
|
||||
pub fn as_bytes(&self) -> &[u8; 32] {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// 转换为十六进制字符串
|
||||
pub fn to_hex(&self) -> String {
|
||||
hex::encode(self.0)
|
||||
}
|
||||
|
||||
/// 从十六进制字符串解析
|
||||
pub fn from_hex(s: &str) -> Result<Self, crate::error::CasError> {
|
||||
let bytes = hex::decode(s).map_err(|_| {
|
||||
crate::error::CasError::Other(anyhow::anyhow!("Invalid hex string"))
|
||||
})?;
|
||||
|
||||
if bytes.len() != 32 {
|
||||
return Err(crate::error::CasError::Other(anyhow::anyhow!(
|
||||
"Hash must be 32 bytes"
|
||||
)));
|
||||
}
|
||||
|
||||
let mut hash_bytes = [0u8; 32];
|
||||
hash_bytes.copy_from_slice(&bytes);
|
||||
Ok(Self(hash_bytes))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Hash {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.to_hex())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Hash {
|
||||
type Err = crate::error::CasError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Self::from_hex(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::Serialize for Hash {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serializer.serialize_str(&self.to_hex())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde::Deserialize<'de> for Hash {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
Self::from_hex(&s).map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
/// 计算数据的 BLAKE3 Hash
|
||||
///
|
||||
/// # 参数
|
||||
/// - `data`: 要计算 Hash 的数据
|
||||
///
|
||||
/// # 返回
|
||||
/// - 数据的 BLAKE3 Hash
|
||||
///
|
||||
/// # 示例
|
||||
/// ```
|
||||
/// use bat_cas_engine::hash::compute_hash;
|
||||
///
|
||||
/// let data = b"Hello, World!";
|
||||
/// let hash = compute_hash(data);
|
||||
/// println!("Hash: {}", hash);
|
||||
/// ```
|
||||
pub fn compute_hash(data: &[u8]) -> Hash {
|
||||
let hash_bytes = blake3::hash(data);
|
||||
Hash::from_bytes(*hash_bytes.as_bytes())
|
||||
}
|
||||
|
||||
/// 计算文件的 BLAKE3 Hash
|
||||
///
|
||||
/// # 参数
|
||||
/// - `path`: 文件路径
|
||||
///
|
||||
/// # 返回
|
||||
/// - 成功:返回文件的 Hash
|
||||
/// - 失败:返回错误
|
||||
pub async fn compute_file_hash(path: &std::path::Path) -> crate::error::Result<Hash> {
|
||||
let data = tokio::fs::read(path).await?;
|
||||
Ok(compute_hash(&data))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_compute_hash() {
|
||||
let data = b"Hello, World!";
|
||||
let hash = compute_hash(data);
|
||||
|
||||
// BLAKE3 Hash 应该是确定性的
|
||||
let hash2 = compute_hash(data);
|
||||
assert_eq!(hash, hash2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hash_to_string() {
|
||||
let data = b"Test data";
|
||||
let hash = compute_hash(data);
|
||||
let hex = hash.to_hex();
|
||||
|
||||
// 应该是 64 个字符的十六进制字符串
|
||||
assert_eq!(hex.len(), 64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hash_from_string() {
|
||||
let data = b"Test data";
|
||||
let hash = compute_hash(data);
|
||||
let hex = hash.to_hex();
|
||||
|
||||
let parsed = Hash::from_hex(&hex).unwrap();
|
||||
assert_eq!(hash, parsed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hash_different_data() {
|
||||
let hash1 = compute_hash(b"Data 1");
|
||||
let hash2 = compute_hash(b"Data 2");
|
||||
|
||||
assert_ne!(hash1, hash2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hash_serialization() {
|
||||
let data = b"Serialization test";
|
||||
let hash = compute_hash(data);
|
||||
|
||||
let json = serde_json::to_string(&hash).unwrap();
|
||||
let deserialized: Hash = serde_json::from_str(&json).unwrap();
|
||||
|
||||
assert_eq!(hash, deserialized);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user