headless_lms_models/
secret.rs1use core::fmt;
19
20use secrecy::{ExposeSecret, SecretString};
21use serde::{Deserialize, Deserializer, Serialize, Serializer};
22
23use sqlx::encode::IsNull;
24use sqlx::postgres::{PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueRef};
25use sqlx::{Decode, Encode, Postgres, Type, error::BoxDynError};
26
27#[derive(Clone)]
33pub struct DbSecret(SecretString);
34
35impl DbSecret {
36 pub fn new(value: impl Into<String>) -> Self {
37 Self(SecretString::new(value.into().into()))
38 }
39}
40
41impl From<String> for DbSecret {
42 fn from(value: String) -> Self {
43 Self::new(value)
44 }
45}
46
47impl From<SecretString> for DbSecret {
48 fn from(value: SecretString) -> Self {
49 Self(value)
50 }
51}
52
53impl From<DbSecret> for SecretString {
54 fn from(value: DbSecret) -> Self {
55 value.0
56 }
57}
58
59impl<'de> Deserialize<'de> for DbSecret {
64 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
65 let s = String::deserialize(deserializer)?;
66 Ok(DbSecret::new(s))
67 }
68}
69
70impl ExposeSecret<str> for DbSecret {
71 fn expose_secret(&self) -> &str {
72 self.0.expose_secret()
73 }
74}
75
76impl fmt::Debug for DbSecret {
77 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78 f.write_str("DbSecret(…redacted…)")
79 }
80}
81
82impl Type<Postgres> for DbSecret {
85 fn type_info() -> PgTypeInfo {
86 <String as Type<Postgres>>::type_info()
87 }
88 fn compatible(ty: &PgTypeInfo) -> bool {
89 <String as Type<Postgres>>::compatible(ty)
90 }
91}
92
93impl PgHasArrayType for DbSecret {
94 fn array_type_info() -> PgTypeInfo {
95 <String as PgHasArrayType>::array_type_info()
96 }
97}
98
99impl<'r> Decode<'r, Postgres> for DbSecret {
100 fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
101 let s = <String as Decode<Postgres>>::decode(value)?;
102 Ok(DbSecret::new(s))
103 }
104}
105
106impl<'q> Encode<'q, Postgres> for DbSecret {
107 fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
108 <&str as Encode<Postgres>>::encode_by_ref(&self.0.expose_secret(), buf)
110 }
111}
112
113#[derive(Clone)]
120pub struct OutboundSecret(SecretString);
121
122impl OutboundSecret {
123 pub fn new(value: impl Into<String>) -> Self {
124 Self(SecretString::new(value.into().into()))
125 }
126}
127
128impl From<String> for OutboundSecret {
129 fn from(value: String) -> Self {
130 Self::new(value)
131 }
132}
133
134impl ExposeSecret<str> for OutboundSecret {
135 fn expose_secret(&self) -> &str {
136 self.0.expose_secret()
137 }
138}
139
140impl fmt::Debug for OutboundSecret {
141 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142 f.write_str("OutboundSecret(…redacted…)")
143 }
144}
145
146impl Serialize for OutboundSecret {
147 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
148 serializer.serialize_str(self.0.expose_secret())
150 }
151}
152
153impl<'de> Deserialize<'de> for OutboundSecret {
154 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
155 let s = String::deserialize(deserializer)?;
156 Ok(OutboundSecret::new(s))
157 }
158}
159
160impl Type<Postgres> for OutboundSecret {
163 fn type_info() -> PgTypeInfo {
164 <String as Type<Postgres>>::type_info()
165 }
166 fn compatible(ty: &PgTypeInfo) -> bool {
167 <String as Type<Postgres>>::compatible(ty)
168 }
169}
170
171impl PgHasArrayType for OutboundSecret {
172 fn array_type_info() -> PgTypeInfo {
173 <String as PgHasArrayType>::array_type_info()
174 }
175}
176
177impl<'r> Decode<'r, Postgres> for OutboundSecret {
178 fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
179 let s = <String as Decode<Postgres>>::decode(value)?;
180 Ok(OutboundSecret::new(s))
181 }
182}
183
184impl<'q> Encode<'q, Postgres> for OutboundSecret {
185 fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
186 <&str as Encode<Postgres>>::encode_by_ref(&self.0.expose_secret(), buf)
187 }
188}