http_authentication/schemes/bearer/
credentials.rs1use alloc::{boxed::Box, format, string::String};
2use core::str::{self, FromStr};
3
4use crate::{schemes::NAME_BEARER as NAME, SP};
5
6#[derive(Debug, Clone)]
8pub struct Credentials {
9 pub token: Box<str>,
10}
11
12impl Credentials {
13 pub fn new(token: impl AsRef<str>) -> Self {
14 Self {
15 token: token.as_ref().into(),
16 }
17 }
18
19 pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Result<Self, CredentialsParseError> {
20 let bytes = bytes.as_ref();
21
22 if bytes.len() < NAME.len() + 1 {
23 return Err(CredentialsParseError::Other("too short"));
24 }
25
26 if !&bytes[..NAME.len()].eq_ignore_ascii_case(NAME.as_bytes()) {
27 return Err(CredentialsParseError::SchemeMismatch);
28 }
29
30 if bytes[NAME.len()..NAME.len() + 1] != [SP as u8] {
31 return Err(CredentialsParseError::OneSPMismatch);
32 }
33
34 let token68_bytes = &bytes[NAME.len() + 1..];
35
36 let token = token68_bytes;
37 let token = str::from_utf8(token).map_err(CredentialsParseError::TokenToStrFailed)?;
38
39 Ok(Self::new(token))
40 }
41
42 fn internal_to_string(&self) -> String {
43 format!("{NAME}{SP}{}", self.token)
44 }
45}
46
47#[derive(Debug)]
49pub enum CredentialsParseError {
50 SchemeMismatch,
51 OneSPMismatch,
52 TokenToStrFailed(str::Utf8Error),
53 Other(&'static str),
54}
55
56impl core::fmt::Display for CredentialsParseError {
57 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
58 write!(f, "{self:?}")
59 }
60}
61
62#[cfg(feature = "std")]
63impl std::error::Error for CredentialsParseError {}
64
65impl core::fmt::Display for Credentials {
67 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
68 write!(f, "{}", self.internal_to_string())
69 }
70}
71
72impl FromStr for Credentials {
74 type Err = CredentialsParseError;
75
76 fn from_str(s: &str) -> Result<Self, Self::Err> {
77 Self::from_bytes(s.as_bytes())
78 }
79}
80
81#[cfg(test)]
85pub(crate) const DEMO_CREDENTIALS_STR: &str = "Bearer mF_9.B5f-4.1JqM";
86#[cfg(test)]
87pub(crate) const DEMO_CREDENTIALS_TOKEN_STR: &str = "mF_9.B5f-4.1JqM";
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92
93 use alloc::string::ToString as _;
94
95 #[test]
96 fn test_parse_and_render() {
97 let c = DEMO_CREDENTIALS_STR.parse::<Credentials>().unwrap();
98 assert_eq!(c.token, DEMO_CREDENTIALS_TOKEN_STR.into());
99 assert_eq!(c.to_string(), DEMO_CREDENTIALS_STR);
100
101 match Credentials::from_str("Bearer") {
103 Err(CredentialsParseError::Other(err)) => {
104 assert_eq!(err, "too short")
105 }
106 x => panic!("{x:?}"),
107 }
108
109 match Credentials::from_str("MyScheme ") {
110 Err(CredentialsParseError::SchemeMismatch) => {}
111 x => panic!("{x:?}"),
112 }
113
114 match Credentials::from_str("Bearer-") {
115 Err(CredentialsParseError::OneSPMismatch) => {}
116 x => panic!("{x:?}"),
117 }
118 }
119}