1use std::fmt;
2
3use serde::{Deserialize, Deserializer};
4use sofie_core::{Cast, ColumnReader, ColumnSpec, Result, SqlValue};
5
6#[derive(Clone)]
8pub struct Secret<T> {
9 value: T,
10}
11
12impl<T> Secret<T> {
13 pub fn new(value: T) -> Self {
14 Self { value }
15 }
16
17 pub fn expose(&self) -> &T {
18 &self.value
19 }
20}
21
22impl<T: AsRef<[u8]>> PartialEq for Secret<T> {
24 fn eq(&self, other: &Self) -> bool {
25 let (own, other) = (self.value.as_ref(), other.value.as_ref());
26 let difference = own
27 .iter()
28 .zip(other)
29 .fold(0, |difference, (own, other)| difference | (own ^ other));
30 own.len() == other.len() && std::hint::black_box(difference) == 0
31 }
32}
33
34impl<T: AsRef<[u8]>> Eq for Secret<T> {}
35
36impl<T> fmt::Debug for Secret<T> {
37 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
38 sofie_core::value::write_masked(formatter, "Secret")
39 }
40}
41
42impl<T: Cast> Cast for Secret<T> {
43 const COLUMNS: &'static [ColumnSpec] = T::COLUMNS;
44 const NULLABLE: bool = T::NULLABLE;
45 const SENSITIVE: bool = true;
46
47 fn encode(&self, out: &mut Vec<SqlValue>) {
48 self.value.encode(out);
49 }
50
51 fn decode(row: &mut dyn ColumnReader) -> Result<Self> {
52 T::decode(row).map(Self::new)
53 }
54}
55
56impl<'de, T: Deserialize<'de>> Deserialize<'de> for Secret<T> {
57 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
58 T::deserialize(deserializer).map(Self::new)
59 }
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65 use crate::testing::round_trip;
66
67 #[test]
68 fn never_prints_its_value() {
69 let key = Secret::new("sk_live_123".to_owned());
70
71 assert_eq!(format!("{key:?}"), "Secret(***)");
72 assert_eq!(format!("{:?}", Some(&key)), "Some(Secret(***))");
73 assert_eq!(key.expose(), "sk_live_123");
74 }
75
76 #[test]
77 fn equals_only_the_same_bytes() {
78 let key = Secret::new("sk_live_123".to_owned());
79
80 assert_eq!(key, Secret::new("sk_live_123".to_owned()));
81 assert_ne!(key, Secret::new("sk_live_124".to_owned()));
82 assert_ne!(key, Secret::new("sk_live_12".to_owned()));
83 }
84
85 #[test]
86 fn is_read_from_input_and_stored_like_its_value() {
87 let key: Secret<String> = serde_json::from_value(serde_json::json!("sk_123")).unwrap();
88 assert_eq!(round_trip(&key), vec![SqlValue::Text("sk_123".into())]);
89 }
90}