mod error;
#[cfg(feature = "keyring")]
pub use keyring;
#[cfg(feature = "keyring")]
use keyring::KeyringEntry;
use log::debug;
#[cfg(feature = "command")]
pub use process;
#[cfg(feature = "command")]
use process::Command;
#[doc(inline)]
pub use crate::error::{Error, Result};
#[derive(Clone, Debug, Default, Eq, PartialEq)]
#[cfg_attr(
feature = "derive",
derive(serde::Serialize, serde::Deserialize),
serde(rename_all = "kebab-case")
)]
pub enum Secret {
Raw(String),
#[cfg(feature = "command")]
#[cfg_attr(feature = "derive", serde(alias = "cmd"))]
Command(Command),
#[cfg(feature = "keyring")]
#[cfg_attr(feature = "derive", serde(rename = "keyring"))]
KeyringEntry(KeyringEntry),
#[default]
#[cfg_attr(feature = "derive", serde(skip_serializing))]
Undefined,
}
impl Secret {
pub fn new() -> Self {
Default::default()
}
pub fn new_raw(raw: impl ToString) -> Self {
Self::Raw(raw.to_string())
}
#[cfg(feature = "command")]
pub fn new_command(cmd: impl Into<Command>) -> Self {
Self::Command(cmd.into())
}
#[cfg(feature = "keyring")]
pub fn new_keyring_entry(entry: KeyringEntry) -> Self {
Self::KeyringEntry(entry)
}
#[cfg(feature = "keyring")]
pub fn try_new_keyring_entry(
entry: impl TryInto<KeyringEntry, Error = keyring::Error>,
) -> Result<Self> {
let entry = entry.try_into().map_err(Error::KeyringError)?;
Ok(Self::KeyringEntry(entry))
}
pub fn is_undefined(&self) -> bool {
matches!(self, Self::Undefined)
}
pub async fn get(&self) -> Result<String> {
match self {
Self::Raw(raw) => Ok(raw.clone()),
#[cfg(feature = "command")]
Self::Command(cmd) => Ok(cmd
.run()
.await
.map_err(Error::GetSecretFromCommand)?
.to_string_lossy()
.lines()
.take(1)
.next()
.ok_or(Error::GetSecretFromCommandEmptyOutputError)?
.to_owned()),
#[cfg(feature = "keyring")]
Self::KeyringEntry(entry) => {
Ok(entry.get_secret().await.map_err(Error::KeyringError)?)
}
Self::Undefined => Err(Error::GetUndefinedSecretError),
}
}
pub async fn find(&self) -> Result<Option<String>> {
match self {
Self::Raw(secret) => Ok(Some(secret.clone())),
#[cfg(feature = "command")]
Self::Command(cmd) => Ok(cmd
.run()
.await
.map_err(Error::GetSecretFromCommand)?
.to_string_lossy()
.lines()
.take(1)
.next()
.map(ToOwned::to_owned)),
#[cfg(feature = "keyring")]
Self::KeyringEntry(entry) => {
Ok(entry.find_secret().await.map_err(Error::KeyringError)?)
}
Self::Undefined => Ok(None),
}
}
pub async fn set(&mut self, secret: impl AsRef<str>) -> Result<String> {
let secret = secret.as_ref();
match self {
Self::Raw(prev) => {
*prev = secret.to_owned();
}
#[cfg(feature = "command")]
Self::Command(_) => {
debug!("cannot change value of command-based secret");
}
#[cfg(feature = "keyring")]
Self::KeyringEntry(entry) => {
entry
.set_secret(secret)
.await
.map_err(Error::KeyringError)?;
}
Self::Undefined => {
debug!("cannot change value of undefined secret");
}
}
Ok(secret.to_owned())
}
#[cfg(feature = "keyring")]
pub async fn set_only_keyring(&self, secret: impl AsRef<str>) -> Result<String> {
let secret = secret.as_ref();
if let Self::KeyringEntry(entry) = self {
entry
.set_secret(secret)
.await
.map_err(Error::KeyringError)?;
}
Ok(secret.to_owned())
}
#[cfg(feature = "keyring")]
pub fn replace_undefined_to_keyring(
&mut self,
entry: impl TryInto<KeyringEntry, Error = keyring::Error>,
) -> Result<()> {
if self.is_undefined() {
*self = Self::try_new_keyring_entry(entry)?
}
Ok(())
}
pub async fn delete(&mut self) -> Result<()> {
#[cfg(feature = "keyring")]
if let Self::KeyringEntry(entry) = self {
entry.delete_secret().await.map_err(Error::KeyringError)?;
}
*self = Self::Undefined;
Ok(())
}
#[cfg(feature = "keyring")]
pub async fn delete_only_keyring(&self) -> Result<()> {
if let Self::KeyringEntry(entry) = self {
entry.delete_secret().await.map_err(Error::KeyringError)?;
}
Ok(())
}
}