1#![no_std]
9#![forbid(unsafe_code, clippy::expect_used, clippy::panic)]
10#![deny(
11 clippy::all,
12 absolute_paths_not_starting_with_crate,
13 deprecated_in_future,
14 missing_copy_implementations,
15 missing_debug_implementations,
16 missing_docs,
17 noop_method_call,
18 rust_2018_compatibility,
19 rust_2018_idioms,
20 rust_2021_compatibility,
21 single_use_lifetimes,
22 trivial_bounds,
23 trivial_casts,
24 trivial_numeric_casts,
25 unreachable_code,
26 unreachable_patterns,
27 unreachable_pub,
28 unstable_features,
29 unused,
30 unused_crate_dependencies,
31 unused_import_braces,
32 unused_lifetimes,
33 unused_qualifications,
34 unused_results,
35 variant_size_differences
36)]
37
38extern crate alloc;
39
40use alloc::vec::Vec;
41
42use base64::Engine;
43use core::fmt::{Debug, Display, Formatter};
44use core::marker::PhantomData;
45use core::ops::{Deref, DerefMut};
46use core::str::FromStr;
47
48use base64::engine::general_purpose::GeneralPurpose;
49use base64::engine::general_purpose::{STANDARD, STANDARD_NO_PAD, URL_SAFE, URL_SAFE_NO_PAD};
50
51mod sealed {
52 pub trait Config {
53 const CONFIG: base64::engine::general_purpose::GeneralPurpose;
54 }
55}
56
57use sealed::Config;
58
59#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
61pub struct Standard(());
62
63impl Config for Standard {
64 const CONFIG: GeneralPurpose = STANDARD;
65}
66
67#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
69pub struct StandardNoPad(());
70
71impl Config for StandardNoPad {
72 const CONFIG: GeneralPurpose = STANDARD_NO_PAD;
73}
74
75#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
77pub struct UrlSafe(());
78
79impl Config for UrlSafe {
80 const CONFIG: GeneralPurpose = URL_SAFE;
81}
82
83#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
85pub struct UrlSafeNoPad(());
86
87impl Config for UrlSafeNoPad {
88 const CONFIG: GeneralPurpose = URL_SAFE_NO_PAD;
89}
90
91#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
93pub struct Bytes<T, C = Standard>(T, PhantomData<C>);
94
95impl<T: Debug, C> Debug for Bytes<T, C> {
96 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
97 f.debug_tuple("Bytes").field(&self.0).finish()
98 }
99}
100
101impl<T: Default, C> Default for Bytes<T, C> {
102 fn default() -> Self {
103 Self(Default::default(), PhantomData)
104 }
105}
106
107impl<T, C> Bytes<T, C> {
108 pub fn into_inner(self) -> T {
110 self.0
111 }
112}
113
114impl<T, C> From<T> for Bytes<T, C> {
115 fn from(value: T) -> Self {
116 Self(value, PhantomData)
117 }
118}
119
120impl<T: AsRef<U>, U: ?Sized, C> AsRef<U> for Bytes<T, C> {
121 fn as_ref(&self) -> &U {
122 self.0.as_ref()
123 }
124}
125
126impl<T: AsMut<U>, U: ?Sized, C> AsMut<U> for Bytes<T, C> {
127 fn as_mut(&mut self) -> &mut U {
128 self.0.as_mut()
129 }
130}
131
132impl<T, C> Deref for Bytes<T, C> {
133 type Target = T;
134
135 fn deref(&self) -> &Self::Target {
136 &self.0
137 }
138}
139
140impl<T, C> DerefMut for Bytes<T, C> {
141 fn deref_mut(&mut self) -> &mut Self::Target {
142 &mut self.0
143 }
144}
145
146impl<T: AsRef<[u8]>, C: Config> Display for Bytes<T, C> {
147 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
148 f.write_str(&C::CONFIG.encode(self.0.as_ref()))
149 }
150}
151
152impl<T: From<Vec<u8>>, C: Config> FromStr for Bytes<T, C> {
153 type Err = base64::DecodeError;
154
155 fn from_str(s: &str) -> Result<Self, Self::Err> {
156 C::CONFIG.decode(s).map(|x| Self(x.into(), PhantomData))
157 }
158}
159
160#[cfg(feature = "serde")]
161impl<T: AsRef<[u8]>, C: Config> serde::Serialize for Bytes<T, C> {
162 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
163 if serializer.is_human_readable() {
164 C::CONFIG.encode(self.0.as_ref()).serialize(serializer)
165 } else {
166 serializer.serialize_bytes(self.0.as_ref())
167 }
168 }
169}
170
171#[cfg(feature = "serde")]
172impl<'de, T: From<Vec<u8>>, C: Config> serde::Deserialize<'de> for Bytes<T, C> {
173 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
174 use serde::de::Error;
175
176 if deserializer.is_human_readable() {
177 let b64 = alloc::borrow::Cow::<'de, str>::deserialize(deserializer)?;
178 let buf = C::CONFIG
179 .decode(b64.as_ref())
180 .map_err(|_| Error::custom("invalid base64"))?;
181 Ok(Self(buf.into(), PhantomData))
182 } else {
183 Ok(Self(Vec::deserialize(deserializer)?.into(), PhantomData))
184 }
185 }
186}