ifunny_basic/
lib.rs

1#![allow(unused)]
2#![forbid(unsafe_code)]
3
4use easy_base64::encode;
5use sha1_smol::Sha1;
6use sha256::digest;
7use uuid::Uuid;
8
9#[derive(Debug, Clone, PartialEq)]
10pub enum BasicTokenLength {
11    Basic112 = 112,
12    Basic156 = 156,
13}
14
15impl TryFrom<usize> for BasicTokenLength {
16    type Error = ();
17
18    fn try_from(value: usize) -> Result<Self, Self::Error> {
19        match value {
20            112 => Ok(BasicTokenLength::Basic112),
21            156 => Ok(BasicTokenLength::Basic156),
22            _ => Err(()),
23        }
24    }
25}
26
27#[derive(Debug, Clone, PartialEq)]
28pub struct BasicToken(String);
29
30impl BasicToken {
31    pub const CLIENT_ID: &str = "MsOIJ39Q28";
32    pub const CLIENT_SECRET: &str = "PTDc3H8a)Vi=UYap";
33    pub const DEFAULT_LENGTH: BasicTokenLength = BasicTokenLength::Basic112;
34
35    pub fn new(client_id: &str, client_secret: &str, length: BasicTokenLength) -> BasicToken {
36        let id = Uuid::new_v4().as_simple().to_string(); // Hypens removed
37
38        let hex = match length {
39            BasicTokenLength::Basic112 => id,
40            BasicTokenLength::Basic156 => sha256::digest(&id), // Hash with Sha256!
41        }
42        .to_uppercase(); // Convert to uppercase
43
44        let prefix = format!("{hex}_{client_id}:");
45
46        let suffix = {
47            let mut hash = Sha1::new();
48            hash.update(format!("{hex}:{client_id}:{client_secret}").as_bytes());
49            format!("{}", hash.digest())
50        };
51
52        Self(encode(format!("{prefix}{suffix}").as_bytes()))
53    }
54}
55
56impl std::fmt::Display for BasicToken {
57    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58        write!(f, "{}", self.0)
59    }
60}
61
62impl Default for BasicToken {
63    fn default() -> Self {
64        Self::new(Self::CLIENT_ID, Self::CLIENT_SECRET, Self::DEFAULT_LENGTH)
65    }
66}
67
68impl From<String> for BasicToken {
69    fn from(token: String) -> Self {
70        BasicToken(token)
71    }
72}
73
74impl From<BasicToken> for String {
75    fn from(token: BasicToken) -> Self {
76        token.0
77    }
78}
79
80impl From<&str> for BasicToken {
81    fn from(token: &str) -> Self {
82        token.to_owned().into()
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89
90    #[test]
91    fn new_makes_112() {
92        let basic = BasicToken::new(
93            BasicToken::CLIENT_ID,
94            BasicToken::CLIENT_SECRET,
95            BasicTokenLength::Basic112,
96        );
97
98        assert_eq!(basic.0.len(), 112);
99    }
100
101    #[test]
102    fn new_makes_156() {
103        let basic = BasicToken::new("client_id", "client_secret", BasicTokenLength::Basic156);
104
105        assert_eq!(basic.0.len(), 156);
106    }
107
108    #[test]
109    fn default_is_unique() {
110        let basic = BasicToken::default();
111        let basic2 = BasicToken::default();
112
113        assert_ne!(basic, basic2);
114    }
115
116    #[test]
117    fn default_is_112() {
118        let basic = BasicToken::default();
119        assert_eq!(basic.0.len(), 112);
120    }
121
122    #[test]
123    fn clones_are_equal() {
124        let basic = BasicToken::default();
125        let basic2 = basic.clone();
126
127        assert_eq!(basic, basic2)
128    }
129
130    #[test]
131    fn debug_includes_token() {
132        let tokens = vec!["Yinkies", "Foo", "Bar"];
133
134        for token in tokens {
135            let basic = BasicToken::from(token);
136            assert_eq!(format!("{basic:?}"), format!("BasicToken(\"{basic}\")"))
137        }
138    }
139
140    #[test]
141    fn test_display() {
142        let basic = BasicToken::default();
143        let basic2 = format!("{}", basic);
144
145        assert_eq!(basic2, basic.0)
146    }
147
148    #[test]
149    fn test_from_string() {
150        assert_eq!(BasicToken::from("Yinkies".to_string()).0, "Yinkies");
151    }
152
153    #[test]
154    fn test_from_str() {
155        assert_eq!(BasicToken::from("Yinkies").0, "Yinkies");
156    }
157
158    #[test]
159    fn test_into_string() {
160        let basic: String = BasicToken::from("Yinkies").into();
161        assert_eq!(basic, "Yinkies");
162    }
163
164    #[test]
165    fn basic_length_try_from_usize_112() {
166        let basic = BasicTokenLength::try_from(112);
167
168        assert!(basic.is_ok());
169    }
170
171    #[test]
172    fn basic_length_try_from_usize_156() {
173        let basic = BasicTokenLength::try_from(156);
174
175        assert!(basic.is_ok());
176    }
177
178    #[test]
179    fn basic_length_try_from_usize_0() {
180        let basic = BasicTokenLength::try_from(0);
181
182        assert!(basic.is_err());
183    }
184
185    #[test]
186    fn basic_length_clone() {
187        let basic = BasicTokenLength::Basic112;
188        let basic2 = basic.clone();
189
190        assert_eq!(basic, basic2);
191    }
192
193    #[test]
194    fn basic_length_display() {
195        let basic = BasicTokenLength::Basic112;
196        let basic2 = format!("{basic:?}");
197
198        assert_eq!(basic2, "Basic112");
199    }
200}