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#[cfg_attr(feature = "cosmwasm",
28 derive(saa_schema::Schemaifier),
29 schemaifier(type = cw_schema::NodeType::Binary)
30)]
31impl Binary {
32 pub const fn new(data: Vec<u8>) -> Self {
34 Self(data)
35 }
36
37 const B64_ENGINE: GeneralPurpose = GeneralPurpose::new(
42 &base64::alphabet::STANDARD,
43 base64::engine::GeneralPurposeConfig::new()
44 .with_decode_padding_mode(base64::engine::DecodePaddingMode::Indifferent),
45 );
46
47 pub fn from_base64(encoded: &str) -> Result<Self, AuthError> {
50 Self::B64_ENGINE
51 .decode(encoded.as_bytes())
52 .map(Binary::from)
53 .map_err(|_| AuthError::generic("invalid base64"))
54 }
55
56 pub fn to_base64(&self) -> String {
59 Self::B64_ENGINE.encode(self.0.as_slice())
60 }
61
62 pub fn as_slice(&self) -> &[u8] {
63 self.0.as_slice()
64 }
65
66 pub fn to_array<const LENGTH: usize>(&self) -> Result<[u8; LENGTH], AuthError> {
67 if self.len() != LENGTH {
68 return Err(AuthError::InvalidLength("Binary".to_string(), LENGTH as u16, self.len() as u16));
69 }
70
71 let mut out: [u8; LENGTH] = [0; LENGTH];
72 out.copy_from_slice(&self.0);
73 Ok(out)
74 }
75}
76
77impl fmt::Display for Binary {
78 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
79 write!(f, "{}", self.to_base64())
80 }
81}
82
83impl fmt::Debug for Binary {
84 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85 write!(f, "Binary(")?;
88 for byte in self.0.iter() {
89 write!(f, "{byte:02x}")?;
90 }
91 write!(f, ")")?;
92 Ok(())
93 }
94}
95
96impl Deref for Binary {
102 type Target = [u8];
103
104 fn deref(&self) -> &Self::Target {
105 self.as_slice()
106 }
107}
108
109impl AsRef<[u8]> for Binary {
110 fn as_ref(&self) -> &[u8] {
111 self.as_slice()
112 }
113}
114
115impl From<&[u8]> for Binary {
117 fn from(binary: &[u8]) -> Self {
118 Self(binary.to_vec())
119 }
120}
121
122impl<const LENGTH: usize> From<&[u8; LENGTH]> for Binary {
124 fn from(source: &[u8; LENGTH]) -> Self {
125 Self(source.to_vec())
126 }
127}
128
129impl<const LENGTH: usize> From<[u8; LENGTH]> for Binary {
131 fn from(source: [u8; LENGTH]) -> Self {
132 Self(source.into())
133 }
134}
135
136impl From<Vec<u8>> for Binary {
137 fn from(vec: Vec<u8>) -> Self {
138 Self(vec)
139 }
140}
141
142impl From<Binary> for Vec<u8> {
143 fn from(original: Binary) -> Vec<u8> {
144 original.0
145 }
146}
147
148impl PartialEq<Vec<u8>> for Binary {
150 fn eq(&self, rhs: &Vec<u8>) -> bool {
151 self.0 == *rhs
153 }
154}
155
156impl PartialEq<Binary> for Vec<u8> {
158 fn eq(&self, rhs: &Binary) -> bool {
159 *self == rhs.0
161 }
162}
163
164impl PartialEq<&[u8]> for Binary {
166 fn eq(&self, rhs: &&[u8]) -> bool {
167 self.as_slice() == *rhs
169 }
170}
171
172impl PartialEq<Binary> for &[u8] {
174 fn eq(&self, rhs: &Binary) -> bool {
175 *self == rhs.as_slice()
177 }
178}
179
180impl<const LENGTH: usize> PartialEq<&[u8; LENGTH]> for Binary {
182 fn eq(&self, rhs: &&[u8; LENGTH]) -> bool {
183 self.as_slice() == rhs.as_slice()
184 }
185}
186
187impl<const LENGTH: usize> PartialEq<Binary> for &[u8; LENGTH] {
189 fn eq(&self, rhs: &Binary) -> bool {
190 self.as_slice() == rhs.as_slice()
191 }
192}
193
194impl<const LENGTH: usize> PartialEq<[u8; LENGTH]> for Binary {
196 fn eq(&self, rhs: &[u8; LENGTH]) -> bool {
197 self.as_slice() == rhs.as_slice()
198 }
199}
200
201impl<const LENGTH: usize> PartialEq<Binary> for [u8; LENGTH] {
203 fn eq(&self, rhs: &Binary) -> bool {
204 self.as_slice() == rhs.as_slice()
205 }
206}
207
208impl Serialize for Binary {
210 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
211 where
212 S: ser::Serializer,
213 {
214 if serializer.is_human_readable() {
215 serializer.serialize_str(&self.to_base64())
216 } else {
217 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.")
218 }
219 }
220}
221
222impl<'de> Deserialize<'de> for Binary {
224 fn deserialize<D>(deserializer: D) -> Result<Binary, D::Error>
225 where
226 D: Deserializer<'de>,
227 {
228 if deserializer.is_human_readable() {
229 deserializer.deserialize_str(Base64Visitor)
230 } else {
231 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.")
232 }
233 }
234}
235
236
237
238struct Base64Visitor;
239
240impl<'de> de::Visitor<'de> for Base64Visitor {
241 type Value = Binary;
242
243 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
244 formatter.write_str("valid base64 encoded string")
245 }
246
247 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
248 where
249 E: de::Error,
250 {
251 match Binary::from_base64(v) {
252 Ok(binary) => Ok(binary),
253 Err(_) => Err(E::custom(format!("invalid base64: {v}"))),
254 }
255 }
256}
257
258
259
260pub fn to_json_binary<T>(data: &T) -> Result<Binary, AuthError>
261where
262 T: Serialize + ?Sized,
263{
264 serde_json_wasm::to_vec(data).map_err(|e| AuthError::generic(e.to_string())).map(Binary)
265}
266
267
268
269pub fn from_json<T: DeserializeOwned>(value: impl AsRef<[u8]>) -> Result<T, AuthError> {
270 serde_json_wasm::from_slice(value.as_ref())
271 .map_err(|e| AuthError::generic(e.to_string()))
272}
273
274
275pub fn to_json_string<T>(data: &T) -> Result<String, AuthError>
276where T: Serialize + ?Sized,{
277 serde_json_wasm::to_string(data).map_err(|e| AuthError::generic(e.to_string()))
278}