1use std::fmt::Display;
2
3#[derive(Debug)]
5pub struct KeyringError(KeyringErrorImpl);
6
7#[derive(Debug)]
8enum KeyringErrorImpl {
9 UnknownBackend {
10 backend: String,
11 },
12 BackendInitFailure {
13 backend: &'static str,
14 cause: KeyringErrorCause,
15 },
16 NoDefaultSigningKey {
17 backend: &'static str,
18 },
19 AccessError {
20 backend: &'static str,
21 entry: KeyringEntry,
22 action: KeyringAction,
23 cause: KeyringErrorCause,
24 },
25}
26
27#[derive(Debug)]
28pub(super) enum KeyringErrorCause {
29 Backend(keyring::Error),
30 Other(anyhow::Error),
31}
32
33#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
34enum KeyringEntry {
35 AuthToken { registry: String },
36 SigningKey { registry: Option<String> },
37}
38
39#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
40pub(super) enum KeyringAction {
41 Open,
42 Get,
43 Set,
44 Delete,
45}
46
47impl From<keyring::Error> for KeyringErrorCause {
48 fn from(error: keyring::Error) -> Self {
49 KeyringErrorCause::Backend(error)
50 }
51}
52
53impl From<anyhow::Error> for KeyringErrorCause {
54 fn from(error: anyhow::Error) -> Self {
55 KeyringErrorCause::Other(error)
56 }
57}
58
59impl KeyringError {
60 pub(super) fn unknown_backend(backend: String) -> Self {
61 KeyringError(KeyringErrorImpl::UnknownBackend { backend })
62 }
63
64 pub(super) fn backend_init_failure(
65 backend: &'static str,
66 cause: impl Into<KeyringErrorCause>,
67 ) -> Self {
68 KeyringError(KeyringErrorImpl::BackendInitFailure {
69 backend,
70 cause: cause.into(),
71 })
72 }
73
74 pub(super) fn no_default_signing_key(backend: &'static str) -> Self {
75 KeyringError(KeyringErrorImpl::NoDefaultSigningKey { backend })
76 }
77
78 pub(super) fn auth_token_access_error(
79 backend: &'static str,
80 registry: &(impl Display + ?Sized),
81 action: KeyringAction,
82 cause: impl Into<KeyringErrorCause>,
83 ) -> Self {
84 KeyringError(KeyringErrorImpl::AccessError {
85 backend,
86 entry: KeyringEntry::AuthToken {
87 registry: registry.to_string(),
88 },
89 action,
90 cause: cause.into(),
91 })
92 }
93
94 pub(super) fn signing_key_access_error(
95 backend: &'static str,
96 registry: Option<&(impl Display + ?Sized)>,
97 action: KeyringAction,
98 cause: impl Into<KeyringErrorCause>,
99 ) -> Self {
100 KeyringError(KeyringErrorImpl::AccessError {
101 backend,
102 entry: KeyringEntry::SigningKey {
103 registry: registry.map(|s| s.to_string()),
104 },
105 action,
106 cause: cause.into(),
107 })
108 }
109}
110
111impl std::fmt::Display for KeyringErrorCause {
112 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113 match self {
114 KeyringErrorCause::Backend(e) => e.fmt(f),
115 KeyringErrorCause::Other(e) => e.fmt(f),
116 }
117 }
118}
119
120impl std::fmt::Display for KeyringError {
121 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122 write!(f, "keyring error: ")?;
123 match &self.0 {
124 KeyringErrorImpl::UnknownBackend { backend } => {
125 write!(f, "unknown backend '{backend}'. Run `warg config --keyring_backend <backend>` to configure a keyring backend supported on this platform.")
126 }
127 KeyringErrorImpl::BackendInitFailure { backend, .. } => {
128 write!(f, "failed to initialize backend '{backend}'.")
129 }
130 KeyringErrorImpl::NoDefaultSigningKey { backend } => {
131 let _ = backend;
132 write!(f, "no default signing key is set. Please create one by running `warg key set <alg:base64>` or `warg key new`")
133 }
134 KeyringErrorImpl::AccessError {
135 backend,
136 entry,
137 action,
138 cause,
139 } => {
140 match *action {
141 KeyringAction::Open => write!(f, "failed to open ")?,
142 KeyringAction::Get => write!(f, "failed to read ")?,
143 KeyringAction::Set => write!(f, "failed to set ")?,
144 KeyringAction::Delete => write!(f, "failed to delete ")?,
145 };
146 match entry {
147 KeyringEntry::AuthToken { registry } => {
148 write!(f, "auth token for registry <{registry}>.")?
149 }
150 KeyringEntry::SigningKey {
151 registry: Some(registry),
152 } => write!(f, "signing key for registry <{registry}>.")?,
153 KeyringEntry::SigningKey { registry: None } => {
154 write!(f, "default signing key.")?
155 }
156 };
157
158 if *backend == "secret-service"
159 && matches!(
160 cause,
161 KeyringErrorCause::Backend(keyring::Error::PlatformFailure(_))
162 )
163 {
164 write!(
165 f,
166 "\nThe 'secret-service' keyring backend failed.
167You may not have a secret service, such as GNOME Keyring or KWallet, installed or configured.
168Consult your OS distribution's documentation for installation instructions.
169
170Alternatively, use `warg config --keyring-backend <backend>` to set a different keyring backend.
171See `warg config --help` for the options."
172 )?;
173 }
174
175 Ok(())
178 }
179 }
180 }
181}
182
183impl std::error::Error for KeyringError {
184 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
185 match &self.0 {
186 KeyringErrorImpl::AccessError { cause, .. }
187 | KeyringErrorImpl::BackendInitFailure { cause, .. } => match cause {
188 KeyringErrorCause::Backend(e) => Some(e),
189 KeyringErrorCause::Other(e) => Some(e.as_ref()),
190 },
191 _ => None,
192 }
193 }
194}