keyring/
lib.rs

1#![cfg_attr(docs_rs, feature(doc_cfg, doc_auto_cfg))]
2#![doc = include_str!("../README.md")]
3
4mod error;
5mod service;
6
7use std::sync::Arc;
8
9pub use keyring_native as native;
10use tracing::debug;
11
12#[doc(inline)]
13pub use crate::{
14    error::{Error, Result},
15    service::{get_global_service_name, set_global_service_name},
16};
17
18#[cfg(any(
19    all(feature = "tokio", feature = "async-std"),
20    not(any(feature = "tokio", feature = "async-std"))
21))]
22compile_error!("Either feature `tokio` or `async-std` must be enabled for this crate.");
23
24#[cfg(any(
25    all(feature = "rustls", feature = "openssl"),
26    not(any(feature = "rustls", feature = "openssl"))
27))]
28compile_error!("Either feature `rustls` or `openssl` must be enabled for this crate.");
29
30/// The representation of a keyring entry.
31///
32/// This struct is a simple wrapper around [`native::Entry`] that
33/// holds a keyring entry key.
34#[derive(Clone, Debug)]
35#[cfg_attr(
36    feature = "derive",
37    derive(serde::Serialize, serde::Deserialize),
38    serde(try_from = "String", into = "String")
39)]
40pub struct KeyringEntry {
41    /// The key used to identify the current keyring entry.
42    pub key: String,
43
44    /// The native keyring entry.
45    entry: Arc<native::Entry>,
46}
47
48impl Eq for KeyringEntry {}
49
50impl PartialEq for KeyringEntry {
51    /// Two keyring entries are considered equal if their key are
52    /// equal.
53    fn eq(&self, other: &Self) -> bool {
54        self.key == other.key
55    }
56}
57
58impl KeyringEntry {
59    /// Creates a new keyring entry from a key.
60    pub fn try_new(key: impl ToString) -> Result<Self> {
61        Self::try_from(key.to_string())
62    }
63
64    /// Gets the secret of the keyring entry.
65    pub async fn get_secret(&self) -> Result<String> {
66        let key = &self.key;
67        debug!(key, "get keyring secret");
68
69        let entry = self.entry.clone();
70        let secret = spawn_blocking(move || entry.get_password())
71            .await?
72            .map_err(|err| Error::GetSecretError(err, key.clone()))?;
73
74        Ok(secret)
75    }
76
77    /// Finds the secret of the keyring entry.
78    ///
79    /// This function is like [`KeyringEntry::get_secret`], except
80    /// that it returns `None` in case the secret cannot be found.
81    pub async fn find_secret(&self) -> Result<Option<String>> {
82        let key = &self.key;
83        debug!(key, "find keyring secret");
84
85        let entry = self.entry.clone();
86        let secret = spawn_blocking(move || entry.get_password()).await?;
87
88        match secret {
89            Err(native::Error::NoEntry) => Ok(None),
90            Err(err) => Err(Error::FindSecretError(err, key.clone())),
91            Ok(secret) => Ok(Some(secret)),
92        }
93    }
94
95    /// (Re)sets the secret of the keyring entry.
96    pub async fn set_secret(&self, secret: impl ToString) -> Result<()> {
97        let key = &self.key;
98        debug!(key, "set keyring secret");
99
100        let secret = secret.to_string();
101        let entry = self.entry.clone();
102        spawn_blocking(move || entry.set_password(&secret))
103            .await?
104            .map_err(|err| Error::SetSecretError(err, key.clone()))?;
105
106        Ok(())
107    }
108
109    /// (Re)sets the secret of the keyring entry, using the builder
110    /// pattern.
111    ///
112    /// This function acts like [`KeyringEntry::set_secret`], except
113    /// that it returns [`Self`] instead of `()`.
114    pub async fn try_with_secret(self, secret: impl ToString) -> Result<Self> {
115        self.set_secret(secret).await?;
116        Ok(self)
117    }
118
119    /// Deletes the secret of the keyring entry.
120    pub async fn delete_secret(&self) -> Result<()> {
121        let key = &self.key;
122        debug!(key, "delete keyring secret");
123
124        let entry = self.entry.clone();
125        spawn_blocking(move || entry.delete_credential())
126            .await?
127            .map_err(|err| Error::DeleteSecretError(err, key.clone()))?;
128
129        Ok(())
130    }
131}
132
133impl TryFrom<String> for KeyringEntry {
134    type Error = Error;
135
136    /// Creates a new keyring entry from a `String`.
137    ///
138    /// This implementation is a wrapper around
139    /// [`native::Entry::new`], where the service name is taken
140    /// globally from [`get_global_service_name`].
141    fn try_from(key: String) -> Result<Self> {
142        let service = get_global_service_name();
143
144        let entry = match native::Entry::new(service, &key) {
145            Ok(entry) => Ok(Arc::new(entry)),
146            Err(err) => Err(Error::BuildEntryError(err, key.clone())),
147        }?;
148
149        Ok(Self { key, entry })
150    }
151}
152
153impl From<KeyringEntry> for String {
154    /// Returns the key of the current keyring entry.
155    fn from(entry: KeyringEntry) -> Self {
156        entry.key
157    }
158}
159
160/// Spawns a blocking task using [`async_std`].
161#[cfg(feature = "async-std")]
162async fn spawn_blocking<T: Send + 'static>(f: impl Fn() -> T + Send + 'static) -> Result<T> {
163    Ok(async_std::task::spawn_blocking(f).await)
164}
165
166/// Spawns a blocking task using [`tokio`].
167#[cfg(feature = "tokio")]
168async fn spawn_blocking<T: Send + 'static>(f: impl Fn() -> T + Send + 'static) -> Result<T> {
169    Ok(tokio::task::spawn_blocking(f).await?)
170}