use crate::{Base64Slice, base64_slice::QuotedBase64};
use alloc::{borrow::ToOwned, vec::Vec};
use base64::Engine;
use core::{borrow::Borrow, 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
}
#[inline]
fn as_base64_slice(&self) -> &Base64Slice {
Base64Slice::new(&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,
symbol,
symbol_value: _,
} => ParseBase64Error::InvalidLastSymbol { offset, byte: symbol },
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(&QuotedBase64(&self.0)).finish()
}
}
impl fmt::Display for Base64Vec {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self.as_base64_slice(), f)
}
}
impl core::ops::Deref for Base64Vec {
type Target = Base64Slice;
#[inline]
fn deref(&self) -> &Self::Target {
self.as_base64_slice()
}
}
impl Borrow<Base64Slice> for Base64Vec {
#[inline]
fn borrow(&self) -> &Base64Slice {
self.as_base64_slice()
}
}
impl ToOwned for Base64Slice {
type Owned = Base64Vec;
#[inline]
fn to_owned(&self) -> Self::Owned {
Base64Vec(self.as_bytes().to_vec())
}
}
impl AsRef<Base64Slice> for Base64Vec {
#[inline]
fn as_ref(&self) -> &Base64Slice {
self.as_base64_slice()
}
}
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
}
}
impl PartialEq<Base64Slice> for Base64Vec {
#[inline]
fn eq(&self, other: &Base64Slice) -> bool {
self.as_base64_slice() == other
}
}
impl PartialEq<&Base64Slice> for Base64Vec {
#[inline]
fn eq(&self, other: &&Base64Slice) -> bool {
self.as_base64_slice() == *other
}
}
impl PartialEq<Base64Vec> for Base64Slice {
#[inline]
fn eq(&self, other: &Base64Vec) -> bool {
self == other.as_base64_slice()
}
}
impl PartialEq<Base64Vec> for &Base64Slice {
#[inline]
fn eq(&self, other: &Base64Vec) -> bool {
*self == other.as_base64_slice()
}
}
#[cfg(feature = "serde")]
mod serde_impls {
use super::Base64Vec;
use crate::Base64Slice;
use alloc::vec::Vec;
use base64::Engine;
use core::fmt;
use serde_core::{Deserializer, Serialize, Serializer, de::Visitor};
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 {
crate::serde_util::deserialize_byte_buf(deserializer)
}
}
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,
{
Base64Slice::new(bytes).serialize(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,
{
self.as_base64_slice().serialize(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::{Base64Slice, 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()
})
}
}
impl JsonSchema for Base64Slice {
fn schema_name() -> String {
Base64Vec::schema_name()
}
fn is_referenceable() -> bool {
Base64Vec::is_referenceable()
}
fn json_schema(generator: &mut SchemaGenerator) -> Schema {
Base64Vec::json_schema(generator)
}
}
}