covert_types/
token.rs

1use std::str::FromStr;
2
3use rand::{distributions::Alphanumeric, thread_rng, Rng};
4use serde::{Deserialize, Serialize};
5
6use crate::error::ApiError;
7
8const TOKEN_LENGTH: usize = 24;
9
10enum TokenType {
11    Service,
12}
13
14impl TokenType {
15    pub fn prefix(&self) -> &'static str {
16        match self {
17            TokenType::Service => "s",
18        }
19    }
20}
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct Token(String);
24
25impl FromStr for Token {
26    type Err = ApiError;
27
28    fn from_str(s: &str) -> Result<Self, Self::Err> {
29        if s.starts_with(TokenType::Service.prefix()) {
30            // TODO: more validation of string
31            Ok(Self(s.to_string()))
32        } else {
33            Err(ApiError::bad_request())
34        }
35    }
36}
37
38impl Token {
39    #[must_use]
40    pub fn new() -> Self {
41        let mut rng = thread_rng();
42        let chars: String = (0..TOKEN_LENGTH)
43            .map(|_| rng.sample(Alphanumeric) as char)
44            .collect();
45        let token = format!("{}.{chars}", TokenType::Service.prefix());
46        Self(token)
47    }
48
49    // Not using the ToString/Display trait to prevent accidental leaks
50    #[allow(clippy::inherent_to_string)]
51    #[must_use]
52    pub fn to_string(&self) -> String {
53        self.0.clone()
54    }
55}
56
57impl Default for Token {
58    fn default() -> Self {
59        Self::new()
60    }
61}