1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
use std::error::Error;
use std::fmt;
use std::str::FromStr;

/// A base68 value as defined in [RFC7235].
///
/// [RFC7235]: https://tools.ietf.org/html/rfc7235#section-2.1
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Token68(pub(crate) String);

impl Token68 {
    /// Constructs a new base68 value.
    #[inline]
    pub fn new(s: &str) -> Result<Token68, InvalidToken68> {
        let trimmed = s.trim_right_matches('=');

        if trimmed.is_empty() {
            return Err(InvalidToken68(()));
        }

        let ok = trimmed.as_bytes().iter().all(|b| match b {
            b'0'...b'9' | b'a'...b'z' | b'A'...b'Z' | b'-' | b'.' | b'_' | b'~' | b'+' | b'/' => {
                true
            }
            _ => false,
        });

        if ok {
            Ok(Token68(s.to_string()))
        } else {
            Err(InvalidToken68(()))
        }
    }

    /// Returns the string the value as a string.
    #[inline]
    pub fn as_str(&self) -> &str {
        &self.0
    }
}

impl fmt::Display for Token68 {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.write_str(self.as_str())
    }
}

impl FromStr for Token68 {
    type Err = InvalidToken68;

    #[inline]
    fn from_str(s: &str) -> Result<Token68, InvalidToken68> {
        Token68::new(s)
    }
}

#[derive(Debug)]
pub struct InvalidToken68(());

impl fmt::Display for InvalidToken68 {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.write_str("invalid base68")
    }
}

impl Error for InvalidToken68 {
    fn description(&self) -> &str {
        "invalid base68"
    }
}