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<'de> Deserialize<'de> for DbSecret {
52 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
53 let s = String::deserialize(deserializer)?;
54 Ok(DbSecret::new(s))
55 }
56}
57
58impl ExposeSecret<str> for DbSecret {
59 fn expose_secret(&self) -> &str {
60 self.0.expose_secret()
61 }
62}
63
64impl fmt::Debug for DbSecret {
65 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66 f.write_str("DbSecret(…redacted…)")
67 }
68}
69
70impl Type<Postgres> for DbSecret {
73 fn type_info() -> PgTypeInfo {
74 <String as Type<Postgres>>::type_info()
75 }
76 fn compatible(ty: &PgTypeInfo) -> bool {
77 <String as Type<Postgres>>::compatible(ty)
78 }
79}
80
81impl PgHasArrayType for DbSecret {
82 fn array_type_info() -> PgTypeInfo {
83 <String as PgHasArrayType>::array_type_info()
84 }
85}
86
87impl<'r> Decode<'r, Postgres> for DbSecret {
88 fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
89 let s = <String as Decode<Postgres>>::decode(value)?;
90 Ok(DbSecret::new(s))
91 }
92}
93
94impl<'q> Encode<'q, Postgres> for DbSecret {
95 fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
96 <&str as Encode<Postgres>>::encode_by_ref(&self.0.expose_secret(), buf)
98 }
99}
100
101#[derive(Clone)]
108pub struct OutboundSecret(SecretString);
109
110impl OutboundSecret {
111 pub fn new(value: impl Into<String>) -> Self {
112 Self(SecretString::new(value.into().into()))
113 }
114}
115
116impl From<String> for OutboundSecret {
117 fn from(value: String) -> Self {
118 Self::new(value)
119 }
120}
121
122impl ExposeSecret<str> for OutboundSecret {
123 fn expose_secret(&self) -> &str {
124 self.0.expose_secret()
125 }
126}
127
128impl fmt::Debug for OutboundSecret {
129 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130 f.write_str("OutboundSecret(…redacted…)")
131 }
132}
133
134impl Serialize for OutboundSecret {
135 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
136 serializer.serialize_str(self.0.expose_secret())
138 }
139}
140
141impl<'de> Deserialize<'de> for OutboundSecret {
142 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
143 let s = String::deserialize(deserializer)?;
144 Ok(OutboundSecret::new(s))
145 }
146}
147
148impl Type<Postgres> for OutboundSecret {
151 fn type_info() -> PgTypeInfo {
152 <String as Type<Postgres>>::type_info()
153 }
154 fn compatible(ty: &PgTypeInfo) -> bool {
155 <String as Type<Postgres>>::compatible(ty)
156 }
157}
158
159impl PgHasArrayType for OutboundSecret {
160 fn array_type_info() -> PgTypeInfo {
161 <String as PgHasArrayType>::array_type_info()
162 }
163}
164
165impl<'r> Decode<'r, Postgres> for OutboundSecret {
166 fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
167 let s = <String as Decode<Postgres>>::decode(value)?;
168 Ok(OutboundSecret::new(s))
169 }
170}
171
172impl<'q> Encode<'q, Postgres> for OutboundSecret {
173 fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
174 <&str as Encode<Postgres>>::encode_by_ref(&self.0.expose_secret(), buf)
175 }
176}