1#![cfg_attr(docs_rs, feature(doc_cfg, doc_auto_cfg))]
2#![doc = include_str!("../README.md")]
3
4#[cfg(feature = "derive")]
5pub(crate) mod derive;
6mod error;
7
8#[cfg(feature = "keyring")]
9pub use keyring;
10#[cfg(feature = "keyring")]
11use keyring::KeyringEntry;
12#[cfg(feature = "command")]
13pub use process;
14#[cfg(feature = "command")]
15use process::Command;
16use tracing::debug;
17
18#[doc(inline)]
19pub use crate::error::{Error, Result};
20
21#[cfg(any(
22 all(feature = "tokio", feature = "async-std"),
23 not(any(feature = "tokio", feature = "async-std"))
24))]
25compile_error!("Either feature `tokio` or `async-std` must be enabled for this crate.");
26
27#[cfg(any(
28 all(feature = "rustls", feature = "openssl"),
29 not(any(feature = "rustls", feature = "openssl"))
30))]
31compile_error!("Either feature `rustls` or `openssl` must be enabled for this crate.");
32
33#[derive(Clone, Debug, Default, Eq, PartialEq)]
38#[cfg_attr(
39 feature = "derive",
40 derive(serde::Serialize, serde::Deserialize),
41 serde(rename_all = "kebab-case", from = "derive::Secret")
42)]
43pub enum Secret {
44 #[default]
46 Empty,
47
48 Raw(String),
53
54 #[cfg(feature = "command")]
61 #[cfg_attr(feature = "derive", serde(alias = "cmd"))]
62 Command(Command),
63
64 #[cfg(feature = "keyring")]
69 Keyring(KeyringEntry),
70}
71
72impl Secret {
73 pub fn new() -> Self {
75 Default::default()
76 }
77
78 pub fn new_raw(raw: impl ToString) -> Self {
80 Self::Raw(raw.to_string())
81 }
82
83 #[cfg(feature = "command")]
85 pub fn new_command(cmd: impl ToString) -> Self {
86 Self::Command(Command::new(cmd))
87 }
88
89 #[cfg(feature = "keyring")]
91 pub fn new_keyring_entry(entry: KeyringEntry) -> Self {
92 Self::Keyring(entry)
93 }
94
95 #[cfg(feature = "keyring")]
97 pub fn try_new_keyring_entry(
98 entry: impl TryInto<KeyringEntry, Error = keyring::Error>,
99 ) -> Result<Self> {
100 let entry = entry.try_into()?;
101 Ok(Self::new_keyring_entry(entry))
102 }
103
104 pub fn is_empty(&self) -> bool {
106 *self == Self::Empty
107 }
108
109 pub async fn get(&self) -> Result<String> {
115 match self {
116 Self::Empty => {
117 return Err(Error::GetEmptySecretError);
118 }
119 Self::Raw(secret) => {
120 return Ok(secret.clone());
121 }
122 #[cfg(feature = "command")]
123 Self::Command(cmd) => {
124 let full_secret = cmd
125 .run()
126 .await
127 .map_err(Error::GetSecretFromCommand)?
128 .to_string_lossy();
129
130 let first_line_secret = full_secret
131 .lines()
132 .take(1)
133 .next()
134 .ok_or(Error::GetSecretFromCommandEmptyOutputError)?
135 .to_owned();
136
137 Ok(first_line_secret)
138 }
139 #[cfg(feature = "keyring")]
140 Self::Keyring(entry) => {
141 let secret = entry.get_secret().await?;
142 Ok(secret)
143 }
144 }
145 }
146
147 pub async fn find(&self) -> Result<Option<String>> {
152 match self {
153 Self::Empty => {
154 return Ok(None);
155 }
156 Self::Raw(secret) => {
157 return Ok(Some(secret.clone()));
158 }
159 #[cfg(feature = "command")]
160 Self::Command(cmd) => {
161 let full_secret = cmd
162 .run()
163 .await
164 .map_err(Error::GetSecretFromCommand)?
165 .to_string_lossy();
166
167 let first_line_secret = full_secret.lines().take(1).next().map(ToOwned::to_owned);
168
169 Ok(first_line_secret)
170 }
171 #[cfg(feature = "keyring")]
172 Self::Keyring(entry) => {
173 let secret = entry.find_secret().await?;
174 Ok(secret)
175 }
176 }
177 }
178
179 pub async fn set(&mut self, secret: impl ToString) -> Result<String> {
185 match self {
186 Self::Raw(prev) => {
187 *prev = secret.to_string();
188 }
189 #[cfg(feature = "command")]
190 Self::Command(_) => {
191 debug!("cannot change value of command-based secret");
192 }
193 #[cfg(feature = "keyring")]
194 Self::Keyring(entry) => entry.set_secret(secret.to_string()).await?,
195 Self::Empty => {
196 debug!("cannot change value of empty secret");
197 }
198 }
199
200 Ok(secret.to_string())
201 }
202
203 #[cfg(feature = "keyring")]
207 pub async fn set_if_keyring(&self, secret: impl ToString) -> Result<String> {
208 if let Self::Keyring(entry) = self {
209 let secret = secret.to_string();
210 entry.set_secret(&secret).await?;
211 return Ok(secret);
212 }
213
214 Ok(secret.to_string())
215 }
216
217 pub async fn delete(&mut self) -> Result<()> {
219 #[cfg(feature = "keyring")]
220 if let Self::Keyring(entry) = self {
221 entry.delete_secret().await?;
222 }
223
224 *self = Self::Empty;
225
226 Ok(())
227 }
228
229 #[cfg(feature = "keyring")]
233 pub async fn delete_if_keyring(&self) -> Result<()> {
234 if let Self::Keyring(entry) = self {
235 entry.delete_secret().await?;
236 }
237
238 Ok(())
239 }
240
241 pub fn replace_if_empty(&mut self, new: Self) {
245 if self.is_empty() {
246 *self = new
247 }
248 }
249
250 #[cfg(feature = "keyring")]
254 pub fn replace_with_keyring_if_empty(&mut self, entry: impl ToString) -> Result<()> {
255 if self.is_empty() {
256 *self = Self::try_new_keyring_entry(entry.to_string())?;
257 }
258
259 Ok(())
260 }
261}