Files
pfm/PostFundManagement.Application/Services/Shared/HashService.cs
T

86 lines
3.5 KiB
C#

using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
namespace PostFundManagement.Application.Services.Shared;
public sealed partial class HashService(IHashids hasher) : IService
{
[GeneratedRegex(@"\A\b[0-9a-fA-F]+\b\Z", RegexOptions.None, matchTimeoutMilliseconds: 100)]
private static partial Regex HexHashRegex { get; }
[GeneratedRegex(@"\A[0-9a-fA-F]{32}\Z", RegexOptions.None, matchTimeoutMilliseconds: 100)]
private static partial Regex Md5Regex { get; }
[GeneratedRegex(@"\A[0-9a-fA-F]{64}\Z", RegexOptions.None, matchTimeoutMilliseconds: 100)]
private static partial Regex Sha256Regex { get; }
public static bool IsMd5Hash(string? value) =>
!string.IsNullOrWhiteSpace(value) && Md5Regex.IsMatch(value);
public static bool IsSha256Hash(string? value) =>
!string.IsNullOrWhiteSpace(value) && Sha256Regex.IsMatch(value);
public static string? StringToSha256Hash(string? input) =>
string.IsNullOrEmpty(input) ? null : Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(input)));
public static string? StreamToSha256Hash(Stream stream) =>
stream is null ? null : Convert.ToHexString(SHA256.HashData(stream));
public static string? BytesToSha256Hash(byte[] bytes) =>
bytes is null ? null : Convert.ToHexString(SHA256.HashData(bytes));
public static Result<string> ToMd5Hash(string input)
{
if (string.IsNullOrEmpty(input))
return Result.Fail<string>(ValidationError.Create("Input content cannot be null or empty for MD5 processing."));
byte[] bytes = MD5.HashData(Encoding.UTF8.GetBytes(input));
return Result.Ok(Convert.ToHexString(bytes).ToLowerInvariant());
}
public Result<string> HashEncodeHex(string input) => string.IsNullOrWhiteSpace(input) || !HexHashRegex.IsMatch(input)
? Result.Fail<string>(ValidationError.Create("Input must be a valid hexadecimal string."))
: Result.Ok(hasher.EncodeHex(input));
public Result<string> HashEncodeIntId(int id) => id < 0
? Result.Fail<string>(ValidationError.Create("Id cannot be negative."))
: Result.Ok(hasher.Encode(id));
public Result<string> HashEncodeLongId(long id) => id < 0
? Result.Fail<string>(ValidationError.Create("Id cannot be negative."))
: Result.Ok(hasher.EncodeLong(id));
public Result<int> DecodeIntIdHash(string hash)
{
if (string.IsNullOrWhiteSpace(hash))
return Result.Fail<int>(ValidationError.Create("Invalid token layout."));
int[] decoded = hasher.Decode(hash);
return decoded.Length == 1
? Result.Ok(decoded[0]) :
Result.Fail<int>(ValidationError.Create("Invalid or modified Int hash token."));
}
public Result<long> DecodeLongIdHash(string hash)
{
if (string.IsNullOrWhiteSpace(hash))
return Result.Fail<long>(ValidationError.Create("Invalid token layout."));
long[] decoded = hasher.DecodeLong(hash);
return decoded.Length == 1 ? Result.Ok(decoded[0]) :
Result.Fail<long>(ValidationError.Create("Invalid or modified Long hash token."));
}
public async Task<Result<string>> DecodeHexHash(string hex) =>
await Result.Try((Func<ValueTask<Result<string>>>)(async () =>
{
string decoded = hasher.DecodeHex(hex);
return string.IsNullOrEmpty(decoded)
? Result.Fail<string>(ValidationError.Create("Invalid or corrupted hex hash."))
: Result.Ok(decoded);
}));
}