http_authentication/
credentials.rs1use alloc::vec::Vec;
2use core::str::FromStr;
3
4use crate::{
5 schemes::{NAME_BASIC, NAME_BEARER, NAME_DIGEST},
6 SP,
7};
8
9#[derive(Debug, Clone)]
11pub enum Credentials {
12 #[cfg(feature = "scheme-basic")]
13 Basic(crate::schemes::basic::Credentials),
14 #[cfg(feature = "scheme-bearer")]
15 Bearer(crate::schemes::bearer::Credentials),
16}
17
18impl Credentials {
19 #[cfg(feature = "scheme-basic")]
21 pub fn basic(user_id: impl AsRef<str>, password: impl AsRef<str>) -> Self {
22 Self::Basic(crate::schemes::basic::Credentials::new(user_id, password))
23 }
24
25 #[cfg(feature = "scheme-basic")]
26 pub fn as_basic(&self) -> Option<&crate::schemes::basic::Credentials> {
27 match self {
28 Self::Basic(c) => Some(c),
29 #[allow(unreachable_patterns)]
30 _ => None,
31 }
32 }
33
34 #[cfg(feature = "scheme-bearer")]
36 pub fn bearer(token: impl AsRef<str>) -> Self {
37 Self::Bearer(crate::schemes::bearer::Credentials::new(token))
38 }
39
40 #[cfg(feature = "scheme-bearer")]
41 pub fn as_bearer(&self) -> Option<&crate::schemes::bearer::Credentials> {
42 match self {
43 Self::Bearer(c) => Some(c),
44 #[allow(unreachable_patterns)]
45 _ => None,
46 }
47 }
48
49 pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Result<Self, CredentialsParseError> {
51 let bytes = bytes.as_ref();
52
53 let scheme = bytes
54 .iter()
55 .take_while(|x| **x != SP as u8)
56 .cloned()
57 .collect::<Vec<_>>();
58 match scheme {
59 x if x.eq_ignore_ascii_case(NAME_BASIC.as_bytes()) => {
60 #[cfg(feature = "scheme-basic")]
61 {
62 crate::schemes::basic::Credentials::from_bytes(bytes)
63 .map(Self::Basic)
64 .map_err(CredentialsParseError::Basic)
65 }
66 #[cfg(not(feature = "scheme-basic"))]
67 {
68 Err(CredentialsParseError::SchemeUnsupported(
69 "Require feature scheme-basic",
70 ))
71 }
72 }
73 x if x.eq_ignore_ascii_case(NAME_BEARER.as_bytes()) => {
74 #[cfg(feature = "scheme-bearer")]
75 {
76 crate::schemes::bearer::Credentials::from_bytes(bytes)
77 .map(Self::Bearer)
78 .map_err(CredentialsParseError::Bearer)
79 }
80 #[cfg(not(feature = "scheme-bearer"))]
81 {
82 Err(CredentialsParseError::SchemeUnsupported(
83 "Require feature scheme-bearer",
84 ))
85 }
86 }
87 x if x.eq_ignore_ascii_case(NAME_DIGEST.as_bytes()) => {
88 Err(CredentialsParseError::SchemeUnsupported("Unimplemented"))
89 }
90 _ => Err(CredentialsParseError::SchemeUnknown),
91 }
92 }
93}
94
95#[derive(Debug)]
97pub enum CredentialsParseError {
98 #[cfg(feature = "scheme-basic")]
99 Basic(crate::schemes::basic::CredentialsParseError),
100 #[cfg(feature = "scheme-bearer")]
101 Bearer(crate::schemes::bearer::CredentialsParseError),
102 SchemeUnknown,
103 SchemeUnsupported(&'static str),
104}
105
106impl core::fmt::Display for CredentialsParseError {
107 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
108 write!(f, "{self:?}")
109 }
110}
111
112#[cfg(feature = "std")]
113impl std::error::Error for CredentialsParseError {}
114
115#[allow(unused_variables)]
117impl core::fmt::Display for Credentials {
118 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
119 match self {
120 #[cfg(feature = "scheme-basic")]
121 Self::Basic(c) => c.fmt(f),
122 #[cfg(feature = "scheme-bearer")]
123 Self::Bearer(c) => c.fmt(f),
124 #[allow(unreachable_patterns)]
125 _ => unimplemented!(),
126 }
127 }
128}
129
130impl FromStr for Credentials {
132 type Err = CredentialsParseError;
133
134 fn from_str(s: &str) -> Result<Self, Self::Err> {
135 Self::from_bytes(s.as_bytes())
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142
143 #[allow(unused_imports)]
144 use alloc::string::ToString as _;
145
146 #[test]
147 fn test_parse_and_render() {
148 #[cfg(feature = "scheme-basic")]
150 {
151 use crate::schemes::basic::{
152 DEMO_CREDENTIALS_PASSWORD_STR, DEMO_CREDENTIALS_STR, DEMO_CREDENTIALS_USER_ID_STR,
153 };
154
155 match DEMO_CREDENTIALS_STR.parse::<Credentials>() {
156 Ok(c) => {
157 let c = c.as_basic().unwrap();
158 assert_eq!(c.user_id, DEMO_CREDENTIALS_USER_ID_STR.into());
159 assert_eq!(c.password, DEMO_CREDENTIALS_PASSWORD_STR.into());
160 assert_eq!(c.to_string(), DEMO_CREDENTIALS_STR);
161 }
162 x => panic!("{x:?}"),
163 }
164 }
165 #[cfg(not(feature = "scheme-basic"))]
166 {
167 match "Basic bar".parse::<Credentials>() {
168 Err(CredentialsParseError::SchemeUnsupported(_)) => {}
169 x => panic!("{x:?}"),
170 }
171 }
172
173 #[cfg(feature = "scheme-bearer")]
175 {
176 use crate::schemes::bearer::{DEMO_CREDENTIALS_STR, DEMO_CREDENTIALS_TOKEN_STR};
177
178 match DEMO_CREDENTIALS_STR.parse::<Credentials>() {
179 Ok(c) => {
180 let c = c.as_bearer().unwrap();
181 assert_eq!(c.token, DEMO_CREDENTIALS_TOKEN_STR.into());
182 assert_eq!(c.to_string(), DEMO_CREDENTIALS_STR);
183 }
184 x => panic!("{x:?}"),
185 }
186 }
187 #[cfg(not(feature = "scheme-bearer"))]
188 {
189 match "Bearer bar".parse::<Credentials>() {
190 Err(CredentialsParseError::SchemeUnsupported(_)) => {}
191 x => panic!("{x:?}"),
192 }
193 }
194
195 match Credentials::from_str("") {
197 Err(CredentialsParseError::SchemeUnknown) => {}
198 x => panic!("{x:?}"),
199 }
200
201 match Credentials::from_str("Foo bar") {
202 Err(CredentialsParseError::SchemeUnknown) => {}
203 x => panic!("{x:?}"),
204 }
205 }
206}