1use core::fmt;
2use core::ops::Deref;
3use base64::engine::{Engine, GeneralPurpose};
4use serde::{de::{self, DeserializeOwned}, ser, Deserialize, Deserializer, Serialize};
5
6use crate::AuthError;
7
8#[derive(Clone, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
9#[cfg_attr(feature = "wasm", derive(
10 saa_schema::schemars::JsonSchema
11))]
12#[cfg_attr(feature = "substrate", derive(
13 saa_schema::scale::Encode,
14 saa_schema::scale::Decode
15))]
16#[cfg_attr(feature = "solana", derive(
17 saa_schema::borsh::BorshSerialize,
18 saa_schema::borsh::BorshDeserialize
19))]
20#[cfg_attr(all(feature = "std", feature="substrate"), derive(
21 saa_schema::scale_info::TypeInfo)
22)]
23pub struct Binary(
24 #[cfg_attr(feature = "wasm", schemars(with = "String"))]
25 Vec<u8>
26);
27
28impl Binary {
29 pub const fn new(data: Vec<u8>) -> Self {
31 Self(data)
32 }
33
34 const B64_ENGINE: GeneralPurpose = GeneralPurpose::new(
39 &base64::alphabet::STANDARD,
40 base64::engine::GeneralPurposeConfig::new()
41 .with_decode_padding_mode(base64::engine::DecodePaddingMode::Indifferent),
42 );
43
44 pub fn from_base64(encoded: &str) -> Result<Self, AuthError> {
47 Self::B64_ENGINE
48 .decode(encoded.as_bytes())
49 .map(Binary::from)
50 .map_err(|_| AuthError::generic("invalid base64"))
51 }
52
53 pub fn to_base64(&self) -> String {
56 Self::B64_ENGINE.encode(self.0.as_slice())
57 }
58
59 pub fn as_slice(&self) -> &[u8] {
60 self.0.as_slice()
61 }
62
63 pub fn to_array<const LENGTH: usize>(&self) -> Result<[u8; LENGTH], AuthError> {
64 if self.len() != LENGTH {
65 return Err(AuthError::InvalidLength("Binary".to_string(), LENGTH as u16, self.len() as u16));
66 }
67
68 let mut out: [u8; LENGTH] = [0; LENGTH];
69 out.copy_from_slice(&self.0);
70 Ok(out)
71 }
72}
73
74impl fmt::Display for Binary {
75 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
76 write!(f, "{}", self.to_base64())
77 }
78}
79
80impl fmt::Debug for Binary {
81 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82 write!(f, "Binary(")?;
85 for byte in self.0.iter() {
86 write!(f, "{byte:02x}")?;
87 }
88 write!(f, ")")?;
89 Ok(())
90 }
91}
92
93impl Deref for Binary {
99 type Target = [u8];
100
101 fn deref(&self) -> &Self::Target {
102 self.as_slice()
103 }
104}
105
106impl AsRef<[u8]> for Binary {
107 fn as_ref(&self) -> &[u8] {
108 self.as_slice()
109 }
110}
111
112impl From<&[u8]> for Binary {
114 fn from(binary: &[u8]) -> Self {
115 Self(binary.to_vec())
116 }
117}
118
119impl<const LENGTH: usize> From<&[u8; LENGTH]> for Binary {
121 fn from(source: &[u8; LENGTH]) -> Self {
122 Self(source.to_vec())
123 }
124}
125
126impl<const LENGTH: usize> From<[u8; LENGTH]> for Binary {
128 fn from(source: [u8; LENGTH]) -> Self {
129 Self(source.into())
130 }
131}
132
133impl From<Vec<u8>> for Binary {
134 fn from(vec: Vec<u8>) -> Self {
135 Self(vec)
136 }
137}
138
139impl From<Binary> for Vec<u8> {
140 fn from(original: Binary) -> Vec<u8> {
141 original.0
142 }
143}
144
145impl PartialEq<Vec<u8>> for Binary {
147 fn eq(&self, rhs: &Vec<u8>) -> bool {
148 self.0 == *rhs
150 }
151}
152
153impl PartialEq<Binary> for Vec<u8> {
155 fn eq(&self, rhs: &Binary) -> bool {
156 *self == rhs.0
158 }
159}
160
161impl PartialEq<&[u8]> for Binary {
163 fn eq(&self, rhs: &&[u8]) -> bool {
164 self.as_slice() == *rhs
166 }
167}
168
169impl PartialEq<Binary> for &[u8] {
171 fn eq(&self, rhs: &Binary) -> bool {
172 *self == rhs.as_slice()
174 }
175}
176
177impl<const LENGTH: usize> PartialEq<&[u8; LENGTH]> for Binary {
179 fn eq(&self, rhs: &&[u8; LENGTH]) -> bool {
180 self.as_slice() == rhs.as_slice()
181 }
182}
183
184impl<const LENGTH: usize> PartialEq<Binary> for &[u8; LENGTH] {
186 fn eq(&self, rhs: &Binary) -> bool {
187 self.as_slice() == rhs.as_slice()
188 }
189}
190
191impl<const LENGTH: usize> PartialEq<[u8; LENGTH]> for Binary {
193 fn eq(&self, rhs: &[u8; LENGTH]) -> bool {
194 self.as_slice() == rhs.as_slice()
195 }
196}
197
198impl<const LENGTH: usize> PartialEq<Binary> for [u8; LENGTH] {
200 fn eq(&self, rhs: &Binary) -> bool {
201 self.as_slice() == rhs.as_slice()
202 }
203}
204
205impl Serialize for Binary {
207 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
208 where
209 S: ser::Serializer,
210 {
211 if serializer.is_human_readable() {
212 serializer.serialize_str(&self.to_base64())
213 } else {
214 panic!("Binary is only intended to be used with JSON serialization for now. If you are hitting this panic please open an issue at https://github.com/CosmWasm/cosmwasm describing your use case.")
215 }
216 }
217}
218
219impl<'de> Deserialize<'de> for Binary {
221 fn deserialize<D>(deserializer: D) -> Result<Binary, D::Error>
222 where
223 D: Deserializer<'de>,
224 {
225 if deserializer.is_human_readable() {
226 deserializer.deserialize_str(Base64Visitor)
227 } else {
228 panic!("Binary is only intended to be used with JSON serialization for now. If you are hitting this panic please open an issue at https://github.com/CosmWasm/cosmwasm describing your use case.")
229 }
230 }
231}
232
233
234
235struct Base64Visitor;
236
237impl<'de> de::Visitor<'de> for Base64Visitor {
238 type Value = Binary;
239
240 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
241 formatter.write_str("valid base64 encoded string")
242 }
243
244 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
245 where
246 E: de::Error,
247 {
248 match Binary::from_base64(v) {
249 Ok(binary) => Ok(binary),
250 Err(_) => Err(E::custom(format!("invalid base64: {v}"))),
251 }
252 }
253}
254
255
256
257pub fn to_json_binary<T>(data: &T) -> Result<Binary, AuthError>
258where
259 T: Serialize + ?Sized,
260{
261 serde_json_wasm::to_vec(data).map_err(|e| AuthError::generic(e.to_string())).map(Binary)
262}
263
264
265
266pub fn from_json<T: DeserializeOwned>(value: impl AsRef<[u8]>) -> Result<T, AuthError> {
267 serde_json_wasm::from_slice(value.as_ref())
268 .map_err(|e| AuthError::generic(e.to_string()))
269}
270
271
272pub fn to_json_string<T>(data: &T) -> Result<String, AuthError>
273where T: Serialize + ?Sized,{
274 serde_json_wasm::to_string(data).map_err(|e| AuthError::generic(e.to_string()))
275}