statsig_rust/hashing/
hash_util.rs1use serde::{de::Error, Deserialize, Deserializer};
2
3use super::{djb2::djb2, memo_sha_256::MemoSha256};
4use std::fmt::Display;
5
6#[derive(Eq, PartialEq)]
7pub enum HashAlgorithm {
8 Djb2,
9 None,
10 Sha256,
11}
12
13impl HashAlgorithm {
14 #[must_use]
15 pub fn from_string(input: &str) -> Option<Self> {
16 match input {
17 "sha256" | "SHA256" | "Sha256" => Some(HashAlgorithm::Sha256),
18 "djb2" | "DJB2" | "Djb2" => Some(HashAlgorithm::Djb2),
19 "none" | "NONE" | "None" => Some(HashAlgorithm::None),
20 _ => None,
21 }
22 }
23}
24
25pub fn opt_bool_to_hashable(input: &Option<bool>) -> u64 {
26 match input {
27 Some(true) => 1,
28 Some(false) => 0,
29 None => 2,
30 }
31}
32
33impl<'de> Deserialize<'de> for HashAlgorithm {
34 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
35 where
36 D: Deserializer<'de>,
37 {
38 let s = String::deserialize(deserializer)?;
39 HashAlgorithm::from_string(&s).ok_or_else(|| D::Error::custom("Invalid hash algorithm"))
40 }
41}
42
43impl Display for HashAlgorithm {
44 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45 match self {
46 HashAlgorithm::Djb2 => write!(f, "djb2"),
47 HashAlgorithm::None => write!(f, "none"),
48 HashAlgorithm::Sha256 => write!(f, "sha256"),
49 }
50 }
51}
52
53pub struct HashUtil {
54 sha_hasher: MemoSha256,
55}
56
57impl Default for HashUtil {
58 fn default() -> Self {
59 Self::new()
60 }
61}
62
63impl HashUtil {
64 #[must_use]
65 pub fn new() -> Self {
66 Self {
67 sha_hasher: MemoSha256::new(),
68 }
69 }
70
71 pub fn hash(&self, input: &str, hash_algorithm: &HashAlgorithm) -> String {
72 match hash_algorithm {
73 HashAlgorithm::Sha256 => self.sha_hasher.hash_string(input),
74 HashAlgorithm::Djb2 => djb2(input),
75 HashAlgorithm::None => input.to_string(),
76 }
77 }
78
79 pub fn sha256(&self, input: &str) -> String {
80 self.sha_hasher.hash_string(input)
81 }
82
83 pub fn sha256_to_u64(&self, input: &str) -> u64 {
84 let hash = self.sha_hasher.hash_string(input);
85
86 let mut hasher_bytes = [0u8; 8];
87 hasher_bytes.copy_from_slice(&hash.as_bytes()[0..8]);
88
89 u64::from_be_bytes(hasher_bytes)
90 }
91
92 pub fn evaluation_hash(&self, input: &String) -> Option<u64> {
93 self.sha_hasher.compute_hash(input)
94 }
95}