1use std::collections::BTreeMap;
3
4use crate::PrivOwnedStr;
5
6#[derive(Clone, Debug, PartialEq, Eq)]
12#[non_exhaustive]
13pub enum AuthenticateError {
14 InsufficientScope {
20 scope: String,
22 },
23
24 #[doc(hidden)]
25 _Custom {
26 errcode: PrivOwnedStr,
27 attributes: AuthenticateAttrs,
28 },
29}
30
31#[doc(hidden)]
32#[derive(Clone, Debug, PartialEq, Eq)]
33pub struct AuthenticateAttrs(BTreeMap<String, String>);
34
35impl AuthenticateError {
36 fn from_str(s: &str) -> Option<Self> {
40 if let Some(val) = s.strip_prefix("Bearer").map(str::trim) {
41 let mut errcode = None;
42 let mut attrs = BTreeMap::new();
43
44 for (key, value) in val
47 .split(',')
48 .filter_map(|attr| attr.trim().split_once('='))
49 .map(|(key, value)| (key, value.trim_matches('"')))
50 {
51 if key == "error" {
52 errcode = Some(value);
53 } else {
54 attrs.insert(key.to_owned(), value.to_owned());
55 }
56 }
57
58 if let Some(errcode) = errcode {
59 let error = if let Some(scope) = attrs.get("scope").filter(|_| errcode == "insufficient_scope") {
60 AuthenticateError::InsufficientScope {
61 scope: scope.to_owned(),
62 }
63 } else {
64 AuthenticateError::_Custom {
65 errcode: PrivOwnedStr(errcode.into()),
66 attributes: AuthenticateAttrs(attrs),
67 }
68 };
69
70 return Some(error);
71 }
72 }
73
74 None
75 }
76}
77
78impl TryFrom<&AuthenticateError> for http::HeaderValue {
79 type Error = http::header::InvalidHeaderValue;
80
81 fn try_from(error: &AuthenticateError) -> Result<Self, Self::Error> {
82 let s = match error {
83 AuthenticateError::InsufficientScope { scope } => {
84 format!("Bearer error=\"insufficient_scope\", scope=\"{scope}\"")
85 }
86 AuthenticateError::_Custom { errcode, attributes } => {
87 let mut s = format!("Bearer error=\"{}\"", errcode.0);
88
89 for (key, value) in attributes.0.iter() {
90 s.push_str(&format!(", {key}=\"{value}\""));
91 }
92
93 s
94 }
95 };
96
97 s.try_into()
98 }
99}