Skip to main content

cratestack_client_rust/remote_refusal/
mod.rs

1//! What a remote refusal says beyond its code (cratestack#1243): the
2//! `WWW-Authenticate` challenges an `AuthRefusal` sends, and one place to
3//! read the decoded error envelope from either error type.
4//!
5//! The envelope's extra members (cratestack#1208) are already decoded:
6//! `CratestackErrorResponse::extra` on REST, `RpcErrorBody::extra` on RPC.
7
8use cratestack_core::CratestackErrorResponse;
9
10use crate::error::ClientError;
11use crate::rpc::error::{RpcClientError, RpcRemoteError};
12use crate::runtime::wire::RuntimeHeader;
13
14mod challenges;
15
16pub use challenges::Challenges;
17
18/// Every `WWW-Authenticate` value in `headers`, raw and in the order they
19/// arrived.
20pub(crate) fn www_authenticate(headers: &[RuntimeHeader]) -> Challenges {
21    headers
22        .iter()
23        .filter(|header| header.name.eq_ignore_ascii_case("www-authenticate"))
24        .map(|header| header.value.clone())
25        .collect::<Vec<_>>()
26        .into()
27}
28
29impl ClientError {
30    /// The decoded error envelope of a [`ClientError::Remote`]: `code`,
31    /// `message`, `details` and the extra members in `extra`. `None` for
32    /// any other variant, or when the body did not decode.
33    pub fn remote_error(&self) -> Option<&CratestackErrorResponse> {
34        match self {
35            ClientError::Remote { error, .. } => error.as_deref(),
36            _ => None,
37        }
38    }
39
40    /// The `WWW-Authenticate` values of a [`ClientError::Remote`], raw and
41    /// in order; empty for any other variant.
42    pub fn challenges(&self) -> &[String] {
43        match self {
44            ClientError::Remote { challenges, .. } => challenges.as_slice(),
45            _ => &[],
46        }
47    }
48}
49
50impl RpcClientError {
51    /// The server's answer, for an [`RpcClientError::Remote`].
52    pub fn remote(&self) -> Option<&RpcRemoteError> {
53        match self {
54            RpcClientError::Remote(remote) => Some(remote),
55            _ => None,
56        }
57    }
58
59    /// The `WWW-Authenticate` values of an [`RpcClientError::Remote`], raw
60    /// and in order; empty for any other variant.
61    pub fn challenges(&self) -> &[String] {
62        self.remote()
63            .map_or(&[], |remote| remote.challenges.as_slice())
64    }
65}
66
67#[cfg(test)]
68mod tests;