#![cfg_attr(docs_rs, feature(doc_cfg, doc_auto_cfg))]
#![doc = include_str!("../README.md")]
mod error;
mod service;
use std::sync::Arc;
pub use native;
use tracing::debug;
#[doc(inline)]
pub use crate::{
error::{Error, Result},
service::{get_global_service_name, set_global_service_name},
};
#[cfg(any(
all(feature = "tokio", feature = "async-std"),
not(any(feature = "tokio", feature = "async-std"))
))]
compile_error!("Either feature `tokio` or `async-std` must be enabled for this crate.");
#[cfg(any(
all(feature = "rustls", feature = "openssl"),
not(any(feature = "rustls", feature = "openssl"))
))]
compile_error!("Either feature `rustls` or `openssl` must be enabled for this crate.");
#[derive(Clone, Debug)]
#[cfg_attr(
feature = "derive",
derive(serde::Serialize, serde::Deserialize),
serde(try_from = "String", into = "String")
)]
pub struct KeyringEntry {
pub key: String,
entry: Arc<native::Entry>,
}
impl Eq for KeyringEntry {}
impl PartialEq for KeyringEntry {
fn eq(&self, other: &Self) -> bool {
self.key == other.key
}
}
impl KeyringEntry {
pub fn try_new(key: impl ToString) -> Result<Self> {
Self::try_from(key.to_string())
}
pub async fn get_secret(&self) -> Result<String> {
let key = &self.key;
debug!(key, "get keyring secret");
let entry = self.entry.clone();
let secret = spawn_blocking(move || entry.get_password())
.await?
.map_err(|err| Error::GetSecretError(err, key.clone()))?;
Ok(secret)
}
pub async fn find_secret(&self) -> Result<Option<String>> {
let key = &self.key;
debug!(key, "find keyring secret");
let entry = self.entry.clone();
let secret = spawn_blocking(move || entry.get_password()).await?;
match secret {
Err(native::Error::NoEntry) => Ok(None),
Err(err) => Err(Error::FindSecretError(err, key.clone())),
Ok(secret) => Ok(Some(secret)),
}
}
pub async fn set_secret(&self, secret: impl ToString) -> Result<()> {
let key = &self.key;
debug!(key, "set keyring secret");
let secret = secret.to_string();
let entry = self.entry.clone();
spawn_blocking(move || entry.set_password(&secret))
.await?
.map_err(|err| Error::SetSecretError(err, key.clone()))?;
Ok(())
}
pub async fn try_with_secret(self, secret: impl ToString) -> Result<Self> {
self.set_secret(secret).await?;
Ok(self)
}
pub async fn delete_secret(&self) -> Result<()> {
let key = &self.key;
debug!(key, "delete keyring secret");
let entry = self.entry.clone();
spawn_blocking(move || entry.delete_credential())
.await?
.map_err(|err| Error::DeleteSecretError(err, key.clone()))?;
Ok(())
}
}
impl TryFrom<String> for KeyringEntry {
type Error = Error;
fn try_from(key: String) -> Result<Self> {
let service = get_global_service_name();
let entry = match native::Entry::new(service, &key) {
Ok(entry) => Ok(Arc::new(entry)),
Err(err) => Err(Error::BuildEntryError(err, key.clone())),
}?;
Ok(Self { key, entry })
}
}
impl From<KeyringEntry> for String {
fn from(entry: KeyringEntry) -> Self {
entry.key
}
}
#[cfg(feature = "async-std")]
async fn spawn_blocking<T: Send + 'static>(f: impl Fn() -> T + Send + 'static) -> Result<T> {
Ok(async_std::task::spawn_blocking(f).await)
}
#[cfg(feature = "tokio")]
async fn spawn_blocking<T: Send + 'static>(f: impl Fn() -> T + Send + 'static) -> Result<T> {
Ok(tokio::task::spawn_blocking(f).await?)
}