1mod hash;
2mod store;
3
4pub use hash::*;
5pub use store::*;
6
7use sha2::Digest;
8
9#[derive(Clone)]
16pub struct Token([u8; 32]);
17
18impl Token {
19 #[must_use]
22 pub fn new(bytes: [u8; 32]) -> Self {
23 Self(bytes)
24 }
25
26 #[must_use]
28 pub fn random() -> Self {
29 Self::new(rand::random())
30 }
31
32 pub fn decode(s: &str) -> Result<Self, DecodeError> {
39 use base64::{DecodeSliceError, Engine as _, engine::general_purpose::URL_SAFE};
40 let mut bytes = [0u8; 32];
41 let num_bytes = URL_SAFE
42 .decode_slice(s, &mut bytes)
43 .map_err(|error| match error {
44 DecodeSliceError::OutputSliceTooSmall => DecodeError::Length,
45 DecodeSliceError::DecodeError(error) => error.into(),
46 })?;
47 if num_bytes != bytes.len() {
48 return Err(DecodeError::Length);
49 }
50 Ok(Self::new(bytes))
51 }
52
53 #[must_use]
56 pub fn encode(&self) -> String {
57 use base64::{Engine as _, engine::general_purpose::URL_SAFE};
58 URL_SAFE.encode(self.0)
59 }
60
61 #[must_use]
64 pub fn hash(&self) -> TokenHash {
65 let mut hasher = sha2::Sha256::new();
66 hasher.update(self.0);
67 TokenHash::new(hasher.finalize().0)
68 }
69
70 #[must_use]
75 pub fn dangerous_as_array(&self) -> &[u8; 32] {
76 &self.0
77 }
78}
79
80impl std::fmt::Debug for Token {
81 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82 f.debug_struct(stringify!(Token)).finish()
83 }
84}
85
86#[derive(Debug, thiserror::Error)]
88pub enum DecodeError {
89 #[error("base64 decoding failed")]
90 Base64(#[from] base64::DecodeError),
91 #[error("invalid number of bytes in token")]
92 Length,
93}