qcs_api_client_common/configuration/
error.rs1use std::collections::BTreeSet;
2use std::{error::Error, path::PathBuf};
3
4use crate::configuration::{oidc::DISCOVERY_REQUIRED_SCOPE, tokens::LoginError};
5
6use super::ClientConfigurationBuilderError;
7use super::secrets::SECRETS_READ_ONLY_VAR;
8
9#[derive(Debug, thiserror::Error)]
11#[non_exhaustive]
12pub enum LoadError {
13 #[error("Failed to load settings: {0}")]
15 Load(Box<dyn Error + Send + Sync + 'static>),
16 #[error("Failed to load value from the environment variable {variable_name}: {message}")]
18 EnvVar {
19 variable_name: String,
21 message: String,
23 },
24 #[error("Failed to load file from path {path:?}: {message}")]
26 Path {
27 path: PathBuf,
29 message: String,
31 },
32 #[error(transparent)]
34 Io(#[from] std::io::Error),
35 #[error("Failed to build the ClientConfiguration: {0}")]
37 Build(#[from] ClientConfigurationBuilderError),
38 #[error("Expected profile {0} in settings.profiles but it does not exist")]
40 ProfileNotFound(String),
41 #[error("Expected auth server {0} in settings.auth_servers but it does not exist")]
43 AuthServerNotFound(String),
44 #[error("Failed to complete login: {0}")]
46 Login(#[from] LoginError),
47 #[cfg(feature = "tracing-config")]
48 #[error("Could not parse tracing filter: {0}")]
51 TracingFilterParseError(#[from] crate::tracing_configuration::TracingFilterError),
52}
53
54impl<E: Error + 'static> From<shellexpand::LookupError<E>> for LoadError {
55 fn from(value: shellexpand::LookupError<E>) -> Self {
56 Self::EnvVar {
57 variable_name: value.var_name,
58 message: value.cause.to_string(),
59 }
60 }
61}
62
63impl From<figment::Error> for LoadError {
64 fn from(value: figment::Error) -> Self {
65 Self::Load(Box::new(value))
66 }
67}
68
69#[derive(Debug, thiserror::Error)]
71pub enum TokenError {
72 #[error("No refresh token is configured within the selected QCS credential.")]
74 NoRefreshToken,
75 #[error("No access token has been requested.")]
77 NoAccessToken,
78 #[error("Requested an access token for a configuration without credentials.")]
80 NoCredentials,
81 #[error("The access token is invalid: {0}")]
83 InvalidAccessToken(jsonwebtoken::errors::Error),
84 #[error("No auth server is configured within the selected QCS credential.")]
86 NoAuthServer,
87 #[error("Error fetching new token from the QCS API: {0}")]
89 Fetch(#[from] qcs_dependencies_client::reqwest::Error),
90 #[error("Failed to request an externally managed access token: {0}")]
92 ExternallyManaged(String),
93 #[error(
95 "Failed to write the new access token to the secrets file. Setting `{SECRETS_READ_ONLY_VAR}=true` in the environment will skip persistence of newly acquired tokens. Error details: {error}"
96 )]
97 Write {
98 error: WriteError,
100 oauth_session: Box<super::OAuthSession>,
104 },
105 #[error("Failed to fetch the OIDC discovery document: {0}")]
107 Discovery(#[from] DiscoveryError),
108}
109
110#[derive(Debug, thiserror::Error)]
112pub enum DiscoveryError {
113 #[error("invalid issuer URL: {0}")]
114 Url(#[from] url::ParseError),
115 #[error("error fetching discovery document: {0}")]
116 Fetch(#[from] qcs_dependencies_client::reqwest::Error),
117 #[error("failed to parse discovery document: {0}")]
118 Json(#[from] serde_json::Error),
119 #[error("issuer URL ({issuer}) is invalid: {reason}")]
120 InvalidIssuer { issuer: String, reason: String },
121 #[error("discovery document is invalid: {reason}")]
122 InvalidDocument { reason: String },
123 #[error("discovery document issuer ({document}) does not match the queried issuer ({query})")]
124 IssuerMismatch { document: String, query: String },
125 #[error("discovery document `supported_scopes` does not include the required minimum scope \"{expected}\", received: {0:?}", expected = DISCOVERY_REQUIRED_SCOPE)]
126 InvalidScopes(BTreeSet<String>),
127}
128
129#[derive(Debug, thiserror::Error)]
131pub enum WriteError {
132 #[error(transparent)]
134 IoWithPath(#[from] IoErrorWithPath),
135 #[error("File could not be read as TOML: {0}")]
137 InvalidToml(#[from] toml_edit::TomlError),
138 #[error("The table `{0}` does not exist.")]
140 MissingTable(String),
141 #[error("Error formatting time: {0}.")]
143 TimeFormat(#[from] time::error::Format),
144 #[error("Error writing or persisting temporary secrets file during access token refresh: {0}")]
146 TempFile(#[from] async_tempfile::Error),
147}
148
149#[derive(Debug)]
151pub enum IoOperation {
152 Open,
153 Read,
154 Write,
155 Rename { dest: PathBuf },
156 GetMetadata,
157 SetPermissions,
158 Flush,
159}
160
161#[derive(Debug, thiserror::Error)]
163#[error("Io error while error performing {operation:?} on {path}: {error}")]
164pub struct IoErrorWithPath {
165 #[source]
166 pub error: std::io::Error,
167 pub path: PathBuf,
168 pub operation: IoOperation,
169}