winbrew_models\shared/
hash.rs1use core::str::FromStr;
8use serde::{Deserialize, Serialize};
9use std::fmt;
10
11use super::error::ModelError;
12
13#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
15#[serde(rename_all = "lowercase")]
16pub enum HashAlgorithm {
17 Md5,
18 Sha1,
19 #[default]
20 Sha256,
21 Sha512,
22}
23
24impl HashAlgorithm {
25 pub fn as_str(self) -> &'static str {
27 match self {
28 Self::Md5 => "md5",
29 Self::Sha1 => "sha1",
30 Self::Sha256 => "sha256",
31 Self::Sha512 => "sha512",
32 }
33 }
34
35 pub fn display_name(self) -> &'static str {
37 match self {
38 Self::Md5 => "MD5",
39 Self::Sha1 => "SHA1",
40 Self::Sha256 => "SHA256",
41 Self::Sha512 => "SHA512",
42 }
43 }
44
45 pub fn expected_len(self) -> usize {
47 match self {
48 Self::Md5 => 32,
49 Self::Sha1 => 40,
50 Self::Sha256 => 64,
51 Self::Sha512 => 128,
52 }
53 }
54
55 pub fn is_legacy(self) -> bool {
57 matches!(self, Self::Md5 | Self::Sha1)
58 }
59
60 pub fn detect(value: &str) -> Option<Self> {
62 let trimmed = value.trim();
63 if trimmed.is_empty() {
64 return None;
65 }
66
67 let lower = trimmed.to_ascii_lowercase();
68
69 for (prefix, algorithm) in [
70 ("sha512:", Self::Sha512),
71 ("sha256:", Self::Sha256),
72 ("sha1:", Self::Sha1),
73 ("md5:", Self::Md5),
74 ] {
75 if lower.starts_with(prefix) {
76 return Some(algorithm);
77 }
78 }
79
80 let candidate = lower
81 .strip_prefix("sha512:")
82 .or_else(|| lower.strip_prefix("sha256:"))
83 .or_else(|| lower.strip_prefix("sha1:"))
84 .or_else(|| lower.strip_prefix("md5:"))
85 .unwrap_or(lower.as_str());
86
87 [Self::Sha512, Self::Sha256, Self::Sha1, Self::Md5]
88 .into_iter()
89 .find(|algorithm| candidate.len() == algorithm.expected_len())
90 }
91}
92
93impl fmt::Display for HashAlgorithm {
94 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95 f.write_str(self.display_name())
96 }
97}
98
99impl FromStr for HashAlgorithm {
100 type Err = ModelError;
101
102 fn from_str(value: &str) -> Result<Self, Self::Err> {
103 match value.trim().to_ascii_lowercase().as_str() {
104 "md5" => Ok(Self::Md5),
105 "sha1" => Ok(Self::Sha1),
106 "sha256" => Ok(Self::Sha256),
107 "sha512" => Ok(Self::Sha512),
108 other => Err(ModelError::invalid_enum_value("hash.algorithm", other)),
109 }
110 }
111}