keepass_ng/db/types/
value.rs1use std::{fmt::Display, ops::Deref};
2
3use secrecy::{ExposeSecret, SecretBox};
4use zeroize::Zeroize;
5
6#[derive(Debug)]
10pub enum Value<T: Zeroize> {
11 Unprotected(T),
13
14 Protected(SecretBox<T>),
16}
17
18impl<T: Zeroize + Display> std::fmt::Display for Value<T> {
19 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20 match self {
21 Value::Unprotected(data) => write!(f, "{}", data),
22 Value::Protected(_) => write!(f, "[redacted]"),
23 }
24 }
25}
26
27impl<T: Zeroize + Default> Default for Value<T> {
28 fn default() -> Self {
29 Value::Unprotected(Default::default())
30 }
31}
32
33impl<T: Zeroize> Value<T> {
34 pub fn unprotected(data: impl Into<T>) -> Self {
36 Value::Unprotected(data.into())
37 }
38
39 pub fn protected(data: impl Into<T>) -> Self {
41 Value::Protected(SecretBox::new(Box::new(data.into())))
42 }
43
44 pub fn is_protected(&self) -> bool {
46 matches!(self, Value::Protected(_))
47 }
48
49 pub fn get(&self) -> &T {
51 match self {
52 Value::Unprotected(data) => data,
53 Value::Protected(data) => data.expose_secret(),
54 }
55 }
56}
57
58impl<T: Zeroize + Clone> Clone for Value<T> {
59 fn clone(&self) -> Self {
60 match self {
61 Value::Unprotected(data) => Value::Unprotected(data.clone()),
62 Value::Protected(data) => Value::Protected(SecretBox::new(Box::new(data.expose_secret().clone()))),
63 }
64 }
65}
66
67impl<T: Zeroize + PartialEq> PartialEq for Value<T> {
68 fn eq(&self, other: &Self) -> bool {
69 match (self, other) {
70 (Value::Unprotected(a), Value::Unprotected(b)) => a == b,
71 (Value::Protected(a), Value::Protected(b)) => a.expose_secret() == b.expose_secret(),
72 _ => false,
73 }
74 }
75}
76
77impl<T: Zeroize + Eq> Eq for Value<T> {}
78
79impl<T: Zeroize> Deref for Value<T> {
80 type Target = T;
81
82 fn deref(&self) -> &Self::Target {
83 self.get()
84 }
85}
86
87#[cfg(feature = "serialization")]
88impl<T: Zeroize + serde::Serialize> serde::Serialize for Value<T> {
89 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
90 where
91 S: serde::Serializer,
92 {
93 match self {
94 Value::Unprotected(data) => data.serialize(serializer),
95 Value::Protected(data) => data.expose_secret().serialize(serializer),
96 }
97 }
98}
99
100#[cfg(test)]
101mod tests {
102 use super::Value;
103
104 #[test]
105 fn test_value() {
106 let unprotected: Value<String> = Value::unprotected("test");
107 let protected: Value<String> = Value::protected("test");
108 assert!(!unprotected.is_protected());
109 assert!(protected.is_protected());
110 assert!(!unprotected.is_empty());
111 assert!(!protected.is_empty());
112
113 assert_eq!(unprotected.get(), "test");
114 assert_eq!(protected.get(), "test");
115
116 assert_eq!(format!("{}", unprotected), "test");
117 assert_eq!(format!("{}", protected), "[redacted]");
118
119 assert_ne!(unprotected, protected);
120 }
121}