rust_i18n_support/
minify_key.rs

1use once_cell::sync::Lazy;
2use siphasher::sip128::SipHasher13;
3use std::borrow::Cow;
4
5/// The default value of `minify_key` feature.
6pub const DEFAULT_MINIFY_KEY: bool = false;
7
8/// The length of auto-generated translation key
9pub const DEFAULT_MINIFY_KEY_LEN: usize = 24;
10
11/// The prefix of auto-generated translation key
12pub const DEFAULT_MINIFY_KEY_PREFIX: &str = "";
13
14/// The minimum length of the value to be generated the translation key
15pub const DEFAULT_MINIFY_KEY_THRESH: usize = 127;
16
17// The hasher for generate the literal translation key
18static TR_KEY_HASHER: Lazy<SipHasher13> = Lazy::new(SipHasher13::new);
19
20/// Calculate a 128-bit siphash of a value.
21pub fn hash128<T: AsRef<[u8]> + ?Sized>(value: &T) -> u128 {
22    TR_KEY_HASHER.hash(value.as_ref()).as_u128()
23}
24
25/// Generate a translation key from a value.
26///
27/// # Arguments
28///
29/// * `value` - The value to be generated.
30/// * `len` - The length of the translation key.
31/// * `prefix` - The prefix of the translation key.
32/// * `threshold` - The minimum length of the value to be generated.
33///
34/// # Returns
35///
36/// * If `value.len() <= threshold` then returns the origin value.
37/// * Otherwise, returns a base62 encoded 128 bits hashed translation key.
38///
39pub fn minify_key<'r>(value: &'r str, len: usize, prefix: &str, threshold: usize) -> Cow<'r, str> {
40    if value.len() <= threshold {
41        return Cow::Borrowed(value);
42    }
43    let encoded = base62::encode(hash128(value));
44    let len = len.min(encoded.len());
45    format!("{}{}", prefix, &encoded[..len]).into()
46}
47
48/// A trait for generating translation key from a value.
49pub trait MinifyKey<'a> {
50    /// Generate translation key from a value.
51    fn minify_key(&'a self, len: usize, prefix: &str, threshold: usize) -> Cow<'a, str>;
52}
53
54impl<'a> MinifyKey<'a> for str {
55    #[inline]
56    fn minify_key(&'a self, len: usize, prefix: &str, threshold: usize) -> Cow<'a, str> {
57        minify_key(self, len, prefix, threshold)
58    }
59}
60
61impl<'a> MinifyKey<'a> for &str {
62    #[inline]
63    fn minify_key(&'a self, len: usize, prefix: &str, threshold: usize) -> Cow<'a, str> {
64        minify_key(self, len, prefix, threshold)
65    }
66}
67
68impl<'a> MinifyKey<'a> for String {
69    #[inline]
70    fn minify_key(&'a self, len: usize, prefix: &str, threshold: usize) -> Cow<'a, str> {
71        if self.len() <= threshold {
72            return Cow::Borrowed(self);
73        }
74        minify_key(self, len, prefix, threshold)
75    }
76}
77
78impl<'a> MinifyKey<'a> for &String {
79    #[inline]
80    fn minify_key(&'a self, len: usize, prefix: &str, threshold: usize) -> Cow<'a, str> {
81        if self.len() <= threshold {
82            return Cow::from(*self);
83        }
84        minify_key(self, len, prefix, threshold)
85    }
86}
87
88impl<'a> MinifyKey<'a> for Cow<'a, str> {
89    #[inline]
90    fn minify_key(&'a self, len: usize, prefix: &str, threshold: usize) -> Cow<'a, str> {
91        if self.len() <= threshold {
92            return Cow::Borrowed(self);
93        }
94        minify_key(self, len, prefix, threshold)
95    }
96}
97
98impl<'a> MinifyKey<'a> for &Cow<'a, str> {
99    #[inline]
100    fn minify_key(&'a self, len: usize, prefix: &str, threshold: usize) -> Cow<'a, str> {
101        if self.len() <= threshold {
102            return Cow::Borrowed(*self);
103        }
104        minify_key(self, len, prefix, threshold)
105    }
106}
107
108#[cfg(test)]
109mod tests {
110    use super::*;
111
112    #[test]
113    fn test_minify_key() {
114        let msg = "Hello, world!";
115        assert_eq!(
116            minify_key(msg, 24, DEFAULT_MINIFY_KEY_PREFIX, 0),
117            "1LokVzuiIrh1xByyZG4wjZ"
118        );
119        assert_eq!(
120            msg.minify_key(24, DEFAULT_MINIFY_KEY_PREFIX, 0),
121            "1LokVzuiIrh1xByyZG4wjZ"
122        );
123        let msg = "Hello, world!".to_string();
124        assert_eq!(
125            minify_key(&msg, 24, DEFAULT_MINIFY_KEY_PREFIX, 0),
126            "1LokVzuiIrh1xByyZG4wjZ"
127        );
128        assert_eq!(
129            msg.minify_key(24, DEFAULT_MINIFY_KEY_PREFIX, 0),
130            "1LokVzuiIrh1xByyZG4wjZ"
131        );
132        assert_eq!(
133            msg.minify_key(24, DEFAULT_MINIFY_KEY_PREFIX, 128),
134            "Hello, world!"
135        );
136        let msg = &msg;
137        assert_eq!(
138            msg.minify_key(24, DEFAULT_MINIFY_KEY_PREFIX, 0),
139            "1LokVzuiIrh1xByyZG4wjZ"
140        );
141        let msg = Cow::Owned("Hello, world!".to_owned());
142        assert_eq!(
143            minify_key(&msg, 24, DEFAULT_MINIFY_KEY_PREFIX, 0),
144            "1LokVzuiIrh1xByyZG4wjZ"
145        );
146        assert_eq!(
147            msg.minify_key(24, DEFAULT_MINIFY_KEY_PREFIX, 0),
148            "1LokVzuiIrh1xByyZG4wjZ"
149        );
150        assert_eq!(
151            msg.minify_key(24, DEFAULT_MINIFY_KEY_PREFIX, 128),
152            "Hello, world!"
153        );
154        assert_eq!("".minify_key(24, DEFAULT_MINIFY_KEY_PREFIX, 0), "");
155        assert_eq!(
156            "1".minify_key(24, DEFAULT_MINIFY_KEY_PREFIX, 0),
157            "knx7vOJBRfzgQvNfEkbEi"
158        );
159        assert_eq!("1".minify_key(24, "t_", 0), "t_knx7vOJBRfzgQvNfEkbEi");
160    }
161}