secure_gate/cloneable/
string.rs

1use crate::Dynamic;
2use zeroize::Zeroize;
3
4#[cfg(feature = "zeroize")]
5#[derive(Clone, Zeroize)]
6#[zeroize(drop)]
7pub struct CloneableStringInner(String);
8
9#[cfg(feature = "zeroize")]
10impl crate::CloneableSecret for CloneableStringInner {}
11
12#[cfg(feature = "zeroize")]
13pub type CloneableString = Dynamic<CloneableStringInner>;
14
15#[cfg(feature = "zeroize")]
16impl CloneableString {
17    #[inline(always)]
18    pub const fn expose_inner(&self) -> &String {
19        &self.expose_secret().0
20    }
21
22    #[inline(always)]
23    pub fn expose_inner_mut(&mut self) -> &mut String {
24        &mut self.expose_secret_mut().0
25    }
26
27    /// Construct a cloneable string secret by building it in a closure.
28    ///
29    /// This minimizes the time the secret spends on the stack:
30    /// - The closure builds a temporary `String`.
31    /// - It is immediately cloned to the heap.
32    /// - The temporary is zeroized before returning.
33    ///
34    /// Use this when reading passwords or tokens from user input.
35    ///
36    /// # Example
37    ///
38    /// ```
39    /// # #[cfg(feature = "zeroize")]
40    /// # {
41    /// use secure_gate::CloneableString;
42    /// use std::io::{self, Write};
43    ///
44    /// fn read_password() -> io::Result<String> {
45    ///     let mut input = String::new();
46    ///     io::stdout().flush()?;
47    ///     io::stdin().read_line(&mut input)?;
48    ///     Ok(input.trim_end().to_string())
49    /// }
50    ///
51    /// let pw = CloneableString::init_with(|| read_password().unwrap());
52    /// # }
53    /// ```
54    #[must_use]
55    pub fn init_with<F>(constructor: F) -> Self
56    where
57        F: FnOnce() -> String,
58    {
59        let mut tmp = constructor();
60        let secret = Self::from(tmp.clone());
61        tmp.zeroize();
62        secret
63    }
64
65    /// Fallible version of `init_with`.
66    ///
67    /// Useful when construction can fail (e.g., I/O errors).
68    pub fn try_init_with<F, E>(constructor: F) -> Result<Self, E>
69    where
70        F: FnOnce() -> Result<String, E>,
71    {
72        let mut tmp = constructor()?;
73        let secret = Self::from(tmp.clone());
74        tmp.zeroize();
75        Ok(secret)
76    }
77}
78
79#[cfg(feature = "zeroize")]
80impl From<String> for CloneableString {
81    fn from(value: String) -> Self {
82        Dynamic::new(CloneableStringInner(value))
83    }
84}
85
86#[cfg(feature = "zeroize")]
87impl From<&str> for CloneableString {
88    fn from(value: &str) -> Self {
89        Self::from(value.to_string())
90    }
91}