warg_client/keyring/
error.rs

1use std::fmt::Display;
2
3/// Error returned when a keyring operation fails.
4#[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                // The above will be followed by further information returned
176                // from `self.source()`.
177                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}