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> {
85 if self.len() != LENGTH {
86 return Err(AuthError::InvalidLength(LENGTH as u16, self.len() as u16));
87 }
88
89 let mut out: [u8; LENGTH] = [0; LENGTH];
90 out.copy_from_slice(&self.0);
91 Ok(out)
92 }
93}
94
95impl fmt::Display for Binary {
96 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
97 write!(f, "{}", self.to_base64())
98 }
99}
100
101impl fmt::Debug for Binary {
102 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103 write!(f, "Binary(")?;
106 for byte in self.0.iter() {
107 write!(f, "{byte:02x}")?;
108 }
109 write!(f, ")")?;
110 Ok(())
111 }
112}
113
114impl Deref for Binary {
120 type Target = [u8];
121
122 fn deref(&self) -> &Self::Target {
123 self.as_slice()
124 }
125}
126
127impl AsRef<[u8]> for Binary {
128 fn as_ref(&self) -> &[u8] {
129 self.as_slice()
130 }
131}
132
133impl From<&[u8]> for Binary {
135 fn from(binary: &[u8]) -> Self {
136 Self(binary.to_vec())
137 }
138}
139
140impl<const LENGTH: usize> From<&[u8; LENGTH]> for Binary {
142 fn from(source: &[u8; LENGTH]) -> Self {
143 Self(source.to_vec())
144 }
145}
146
147impl<const LENGTH: usize> From<[u8; LENGTH]> for Binary {
149 fn from(source: [u8; LENGTH]) -> Self {
150 Self(source.into())
151 }
152}
153
154impl From<Vec<u8>> for Binary {
155 fn from(vec: Vec<u8>) -> Self {
156 Self(vec)
157 }
158}
159
160impl From<Binary> for Vec<u8> {
161 fn from(original: Binary) -> Vec<u8> {
162 original.0
163 }
164}
165
166impl PartialEq<Vec<u8>> for Binary {
168 fn eq(&self, rhs: &Vec<u8>) -> bool {
169 self.0 == *rhs
171 }
172}
173
174impl PartialEq<Binary> for Vec<u8> {
176 fn eq(&self, rhs: &Binary) -> bool {
177 *self == rhs.0
179 }
180}
181
182impl PartialEq<&[u8]> for Binary {
184 fn eq(&self, rhs: &&[u8]) -> bool {
185 self.as_slice() == *rhs
187 }
188}
189
190impl PartialEq<Binary> for &[u8] {
192 fn eq(&self, rhs: &Binary) -> bool {
193 *self == rhs.as_slice()
195 }
196}
197
198impl<const LENGTH: usize> PartialEq<&[u8; LENGTH]> for Binary {
200 fn eq(&self, rhs: &&[u8; LENGTH]) -> bool {
201 self.as_slice() == rhs.as_slice()
202 }
203}
204
205impl<const LENGTH: usize> PartialEq<Binary> for &[u8; LENGTH] {
207 fn eq(&self, rhs: &Binary) -> bool {
208 self.as_slice() == rhs.as_slice()
209 }
210}
211
212impl<const LENGTH: usize> PartialEq<[u8; LENGTH]> for Binary {
214 fn eq(&self, rhs: &[u8; LENGTH]) -> bool {
215 self.as_slice() == rhs.as_slice()
216 }
217}
218
219impl<const LENGTH: usize> PartialEq<Binary> for [u8; LENGTH] {
221 fn eq(&self, rhs: &Binary) -> bool {
222 self.as_slice() == rhs.as_slice()
223 }
224}
225
226impl Serialize for Binary {
228 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
229 where
230 S: ser::Serializer,
231 {
232 if serializer.is_human_readable() {
233 serializer.serialize_str(&self.to_base64())
234 } else {
235 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.")
236 }
237 }
238}
239
240impl<'de> Deserialize<'de> for Binary {
242 fn deserialize<D>(deserializer: D) -> Result<Binary, D::Error>
243 where
244 D: Deserializer<'de>,
245 {
246 if deserializer.is_human_readable() {
247 deserializer.deserialize_str(Base64Visitor)
248 } else {
249 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.")
250 }
251 }
252}
253
254
255
256struct Base64Visitor;
257
258impl<'de> de::Visitor<'de> for Base64Visitor {
259 type Value = Binary;
260
261 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
262 formatter.write_str("valid base64 encoded string")
263 }
264
265 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
266 where
267 E: de::Error,
268 {
269 match Binary::from_base64(v) {
270 Ok(binary) => Ok(binary),
271 Err(_) => Err(E::custom(format!("invalid base64: {v}"))),
272 }
273 }
274}
275
276#[cfg(feature = "wasm")]
277impl From<Binary> for crate::cosmwasm::Binary {
278 fn from(binary: Binary) -> Self {
279 binary.to_vec().into()
280 }
281}
282
283#[cfg(feature = "wasm")]
284impl Into<Binary> for crate::cosmwasm::Binary {
285 fn into(self) -> Binary {
286 self.to_vec().into()
287 }
288}
289
290
291
292pub fn to_json_binary<T>(data: &T) -> Result<Binary, AuthError>
293where
294 T: Serialize + ?Sized,
295{
296 serde_json_wasm::to_vec(data).map_err(|e| AuthError::generic(e.to_string())).map(Binary)
297}
298
299
300pub fn from_json<T: DeserializeOwned>(value: impl AsRef<[u8]>) -> Result<T, AuthError> {
301 serde_json_wasm::from_slice(value.as_ref())
302 .map_err(|e| AuthError::generic(e.to_string()))
303}