use alloc::vec::Vec;
use base64::Engine;
use core::{error, fmt, str::FromStr};
#[must_use]
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct Base64Vec(pub Vec<u8>);
impl Base64Vec {
#[inline]
pub fn new(bytes: Vec<u8>) -> Self {
Self(bytes)
}
#[inline]
#[must_use]
pub fn into_inner(self) -> Vec<u8> {
self.0
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ParseBase64Error {
InvalidByte {
offset: usize,
byte: u8,
},
InvalidLength {
length: usize,
},
InvalidLastSymbol {
offset: usize,
byte: u8,
},
InvalidPadding,
}
impl fmt::Display for ParseBase64Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ParseBase64Error::InvalidByte { offset, byte } => {
write!(f, "invalid base64 symbol {byte}, offset {offset}",)
}
ParseBase64Error::InvalidLength { length } => {
write!(f, "invalid base64 input length: {length}",)
}
ParseBase64Error::InvalidLastSymbol { offset, byte } => {
write!(
f,
"invalid base64 last symbol {byte}, \
offset {offset}",
)
}
ParseBase64Error::InvalidPadding => {
write!(f, "invalid base64 padding")
}
}
}
}
impl error::Error for ParseBase64Error {}
fn from_decode_error(e: base64::DecodeError) -> ParseBase64Error {
match e {
base64::DecodeError::InvalidByte(offset, byte) => {
ParseBase64Error::InvalidByte { offset, byte }
}
base64::DecodeError::InvalidLength(length) => {
ParseBase64Error::InvalidLength { length }
}
base64::DecodeError::InvalidLastSymbol(offset, byte) => {
ParseBase64Error::InvalidLastSymbol { offset, byte }
}
base64::DecodeError::InvalidPadding => ParseBase64Error::InvalidPadding,
}
}
impl FromStr for Base64Vec {
type Err = ParseBase64Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
base64::engine::general_purpose::STANDARD
.decode(s)
.map(Self)
.map_err(from_decode_error)
}
}
impl fmt::Debug for Base64Vec {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Base64Vec")
.field(&base64::engine::general_purpose::STANDARD.encode(&self.0))
.finish()
}
}
impl fmt::Display for Base64Vec {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
base64::engine::general_purpose::STANDARD.encode(&self.0).fmt(f)
}
}
impl core::ops::Deref for Base64Vec {
type Target = Vec<u8>;
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl core::ops::DerefMut for Base64Vec {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl AsRef<[u8]> for Base64Vec {
#[inline]
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl AsMut<[u8]> for Base64Vec {
#[inline]
fn as_mut(&mut self) -> &mut [u8] {
&mut self.0
}
}
impl From<Vec<u8>> for Base64Vec {
#[inline]
fn from(bytes: Vec<u8>) -> Self {
Self(bytes)
}
}
impl From<Base64Vec> for Vec<u8> {
#[inline]
fn from(base64_vec: Base64Vec) -> Self {
base64_vec.0
}
}
#[cfg(feature = "serde")]
mod serde_impls {
use super::Base64Vec;
use alloc::vec::Vec;
use base64::Engine;
use core::fmt;
use serde_core::{
Deserializer, Serializer,
de::{SeqAccess, Visitor},
};
fn serialize_bytes<S>(
bytes: &[u8],
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if serializer.is_human_readable() {
let encoded =
base64::engine::general_purpose::STANDARD.encode(bytes);
serializer.serialize_str(&encoded)
} else {
serializer.serialize_bytes(bytes)
}
}
fn deserialize_bytes<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
where
D: Deserializer<'de>,
{
use serde_core::de::Error;
if deserializer.is_human_readable() {
struct Base64Visitor;
impl<'de2> Visitor<'de2> for Base64Visitor {
type Value = Vec<u8>;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "a base64-encoded string")
}
fn visit_str<E>(self, data: &str) -> Result<Self::Value, E>
where
E: Error,
{
base64::engine::general_purpose::STANDARD
.decode(data)
.map_err(Error::custom)
}
}
deserializer.deserialize_str(Base64Visitor)
} else {
struct BytesVisitor;
impl<'de2> Visitor<'de2> for BytesVisitor {
type Value = Vec<u8>;
fn expecting(
&self,
formatter: &mut fmt::Formatter,
) -> fmt::Result {
write!(formatter, "a byte array")
}
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
where
E: Error,
{
Ok(v.to_vec())
}
fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
where
E: Error,
{
Ok(v)
}
fn visit_seq<A>(
self,
mut seq: A,
) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de2>,
{
let hint = seq.size_hint().unwrap_or(0);
let mut out = Vec::with_capacity(hint.min(4096));
while let Some(byte) = seq.next_element()? {
out.push(byte);
}
Ok(out)
}
}
deserializer.deserialize_bytes(BytesVisitor)
}
}
impl Base64Vec {
#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
pub fn serialize<S>(
bytes: &[u8],
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serialize_bytes(bytes, serializer)
}
#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
where
D: Deserializer<'de>,
{
deserialize_bytes(deserializer)
}
}
#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
impl serde_core::Serialize for Base64Vec {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serialize_bytes(&self.0, serializer)
}
}
#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
impl<'de> serde_core::Deserialize<'de> for Base64Vec {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserialize_bytes(deserializer).map(Self)
}
}
}
#[cfg(feature = "schemars08")]
mod schemars_impls {
use super::Base64Vec;
use crate::schemars_util::x_rust_type_extension;
use alloc::string::String;
use schemars08::{
JsonSchema,
r#gen::SchemaGenerator,
schema::{InstanceType, Schema, SchemaObject},
};
impl JsonSchema for Base64Vec {
fn schema_name() -> String {
"Base64Vec".into()
}
fn is_referenceable() -> bool {
false
}
fn json_schema(_generator: &mut SchemaGenerator) -> Schema {
let mut extensions = x_rust_type_extension("Base64Vec");
extensions.insert("contentEncoding".into(), "base64".into());
Schema::Object(SchemaObject {
instance_type: Some(InstanceType::String.into()),
format: Some("byte".into()),
extensions,
..Default::default()
})
}
}
}