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