secure_gate/
dynamic.rs

1// src/dynamic.rs
2//! Heap-allocated secure wrappers for dynamic secrets.
3//!
4//! `Dynamic<T>` is a zero-cost wrapper around `Box<T>` that:
5//! - Prevents accidental cloning/leaking via `Debug` redaction.
6//! - Provides explicit access via `.expose_secret()` (canonical API).
7//! - Supports idiomatic `.into()` conversions from owned values.
8//! - Works seamlessly with [`dynamic_alias!`] for type aliases.
9//!
10//! # Examples
11//!
12//! ```
13//! use secure_gate::{dynamic_alias, Dynamic};
14//!
15//! dynamic_alias!(Password, String);
16//!
17//! let pw: Password = "hunter2".into();
18//! assert_eq!(pw.expose_secret(), "hunter2");
19//! ```
20
21extern crate alloc;
22
23use alloc::boxed::Box;
24use core::ops::{Deref, DerefMut};
25
26/// A zero-cost, heap-allocated wrapper for sensitive data.
27///
28/// `Dynamic<T>` stores its value on the heap via `Box<T>`. It behaves like `T`
29/// thanks to `Deref`/`DerefMut`, but redacts itself in debug output and requires
30/// explicit access to the inner value.
31///
32/// Use this for dynamic-sized secrets like passwords or variable-length keys.
33///
34/// # Examples
35///
36/// ```
37/// use secure_gate::Dynamic;
38///
39/// let secret: Dynamic<Vec<u8>> = vec![1, 2, 3].into();
40/// assert_eq!(secret.expose_secret(), &[1, 2, 3]);
41/// ```
42pub struct Dynamic<T: ?Sized>(pub Box<T>);
43
44impl<T: ?Sized> Dynamic<T> {
45    /// Creates a new `Dynamic` from a boxed value.
46    ///
47    /// # Examples
48    ///
49    /// ```
50    /// use secure_gate::Dynamic;
51    ///
52    /// let secret = Dynamic::new_boxed(Box::new("hello".to_string()));
53    /// assert_eq!(secret.expose_secret(), "hello");
54    /// ```
55    #[inline(always)]
56    pub fn new_boxed(value: Box<T>) -> Self {
57        Dynamic(value)
58    }
59
60    /// Creates a new `Dynamic` from a value that can be converted into `Box<T>`.
61    ///
62    /// # Examples
63    ///
64    /// ```
65    /// use secure_gate::Dynamic;
66    ///
67    /// let secret: Dynamic<String> = Dynamic::new("hunter2".to_string());
68    /// assert_eq!(secret.expose_secret(), "hunter2");
69    /// ```
70    #[inline(always)]
71    pub fn new<U>(value: U) -> Self
72    where
73        U: Into<Box<T>>,
74    {
75        Dynamic(value.into())
76    }
77}
78
79impl<T: ?Sized> Deref for Dynamic<T> {
80    type Target = T;
81
82    /// Dereferences the wrapper to access the inner value immutably.
83    #[inline(always)]
84    fn deref(&self) -> &T {
85        &self.0
86    }
87}
88
89impl<T: ?Sized> DerefMut for Dynamic<T> {
90    /// Dereferences the wrapper mutably to access the inner value.
91    #[inline(always)]
92    fn deref_mut(&mut self) -> &mut T {
93        &mut self.0
94    }
95}
96
97impl<T: ?Sized> core::fmt::Debug for Dynamic<T> {
98    /// Formats the value as "[REDACTED]" to prevent leakage in debug output.
99    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
100        f.write_str("[REDACTED]")
101    }
102}
103
104impl<T: ?Sized> Dynamic<T> {
105    /// Accesses the secret value immutably.
106    ///
107    /// This is the canonical, explicit way to read the secret.
108    ///
109    /// # Examples
110    ///
111    /// ```
112    /// use secure_gate::Dynamic;
113    ///
114    /// let secret: Dynamic<String> = "secret".into();
115    /// assert_eq!(secret.expose_secret(), "secret");
116    /// ```
117    #[inline(always)]
118    pub fn expose_secret(&self) -> &T {
119        &self.0
120    }
121
122    /// Accesses the secret value mutably.
123    ///
124    /// Use for in-place modifications.
125    ///
126    /// # Examples
127    ///
128    /// ```
129    /// use secure_gate::Dynamic;
130    ///
131    /// let mut secret: Dynamic<String> = "hello".into();
132    /// secret.expose_secret_mut().push('!');
133    /// assert_eq!(secret.expose_secret(), "hello!");
134    /// ```
135    #[inline(always)]
136    pub fn expose_secret_mut(&mut self) -> &mut T {
137        &mut self.0
138    }
139
140    /// **Deprecated**: Use [`expose_secret`] instead.
141    ///
142    /// This method forwards to [`expose_secret`] for compatibility.
143    #[deprecated(since = "0.5.5", note = "use `expose_secret` instead")]
144    #[doc(hidden)]
145    #[inline(always)]
146    pub fn view(&self) -> &T {
147        self.expose_secret()
148    }
149
150    /// **Deprecated**: Use [`expose_secret_mut`] instead.
151    ///
152    /// This method forwards to [`expose_secret_mut`] for compatibility.
153    #[deprecated(since = "0.5.5", note = "use `expose_secret_mut` instead")]
154    #[doc(hidden)]
155    #[inline(always)]
156    pub fn view_mut(&mut self) -> &mut T {
157        self.expose_secret_mut()
158    }
159
160    /// Consumes the wrapper and returns the inner boxed value.
161    ///
162    /// # Examples
163    ///
164    /// ```
165    /// use secure_gate::Dynamic;
166    ///
167    /// let secret: Dynamic<String> = "owned".into();
168    /// let owned: Box<String> = secret.into_inner();
169    /// assert_eq!(&*owned, "owned");
170    /// ```
171    #[inline(always)]
172    pub fn into_inner(self) -> Box<T> {
173        self.0
174    }
175}
176
177// Clone impls
178#[cfg(not(feature = "zeroize"))]
179impl<T: Clone> Clone for Dynamic<T> {
180    /// Clones the wrapper, cloning the inner value.
181    #[inline(always)]
182    fn clone(&self) -> Self {
183        Dynamic(self.0.clone())
184    }
185}
186
187#[cfg(feature = "zeroize")]
188impl<T: Clone + zeroize::Zeroize> Clone for Dynamic<T> {
189    /// Clones the wrapper, cloning the inner value.
190    #[inline(always)]
191    fn clone(&self) -> Self {
192        Dynamic(self.0.clone())
193    }
194}
195
196impl Dynamic<String> {
197    /// Shrinks the string's capacity to fit its length and returns a mutable reference.
198    ///
199    /// Use this to eliminate slack memory after mutations.
200    ///
201    /// # Examples
202    ///
203    /// ```
204    /// use secure_gate::Dynamic;
205    ///
206    /// let mut secret: Dynamic<String> = String::with_capacity(100).into();
207    /// secret.push_str("short");
208    /// let s: &mut String = secret.finish_mut();
209    /// assert_eq!(s.capacity(), 5);
210    /// ```
211    pub fn finish_mut(&mut self) -> &mut String {
212        let s = &mut **self;
213        s.shrink_to_fit();
214        s
215    }
216}
217
218impl Dynamic<Vec<u8>> {
219    /// Shrinks the vector's capacity to fit its length and returns a mutable reference.
220    ///
221    /// Use this to eliminate slack memory after mutations.
222    ///
223    /// # Examples
224    ///
225    /// ```
226    /// use secure_gate::Dynamic;
227    ///
228    /// let mut secret: Dynamic<Vec<u8>> = Vec::with_capacity(100).into();
229    /// secret.extend_from_slice(b"short");
230    /// let v: &mut Vec<u8> = secret.finish_mut();
231    /// assert_eq!(v.capacity(), 5);
232    /// ```
233    pub fn finish_mut(&mut self) -> &mut Vec<u8> {
234        let v = &mut **self;
235        v.shrink_to_fit();
236        v
237    }
238}
239
240// ——— .into() ergonomics ———
241/// Converts an owned value into a `Dynamic`.
242///
243/// # Examples
244///
245/// ```
246/// use secure_gate::Dynamic;
247///
248/// let secret: Dynamic<Vec<u8>> = vec![1, 2, 3].into();
249/// assert_eq!(secret.expose_secret(), &[1, 2, 3]);
250/// ```
251impl<T> From<T> for Dynamic<T>
252where
253    T: Sized,
254{
255    #[inline(always)]
256    fn from(value: T) -> Self {
257        Self(Box::new(value))
258    }
259}
260
261/// Converts a `Box<T>` into a `Dynamic<T>`.
262impl<T: ?Sized> From<Box<T>> for Dynamic<T> {
263    #[inline(always)]
264    fn from(boxed: Box<T>) -> Self {
265        Self(boxed)
266    }
267}
268
269/// Convenience conversion from `&str` to `Dynamic<String>`.
270///
271/// # Examples
272///
273/// ```
274/// use secure_gate::Dynamic;
275///
276/// let secret: Dynamic<String> = "password".into();
277/// assert_eq!(secret.expose_secret(), "password");
278/// ```
279impl From<&str> for Dynamic<String> {
280    #[inline(always)]
281    fn from(s: &str) -> Self {
282        Self(Box::new(s.to_string()))
283    }
284}
285
286// ───── Add PartialEq and Eq impls for Dynamic ─────
287/// Implements PartialEq for Dynamic<T> where T implements PartialEq.
288///
289/// This enables comparison on Dynamic types like Dynamic<String> or Dynamic<Vec<u8>>.
290impl<T: PartialEq + ?Sized> PartialEq for Dynamic<T> {
291    #[inline(always)]
292    fn eq(&self, other: &Self) -> bool {
293        **self == **other
294    }
295}
296
297/// Implements Eq for Dynamic<T> where T implements Eq.
298impl<T: Eq + ?Sized> Eq for Dynamic<T> {}