Skip to main content

qubit_codec_misc/
base64_codec.rs

1// =============================================================================
2//    Copyright (c) 2026 Haixing Hu.
3//
4//    SPDX-License-Identifier: Apache-2.0
5//
6//    Licensed under the Apache License, Version 2.0.
7// =============================================================================
8//! Base64 byte codec.
9
10use ::base64::Engine;
11use ::base64::engine::GeneralPurpose;
12use ::base64::engine::general_purpose::STANDARD;
13use ::base64::engine::general_purpose::STANDARD_NO_PAD;
14use ::base64::engine::general_purpose::URL_SAFE;
15use ::base64::engine::general_purpose::URL_SAFE_NO_PAD;
16use qubit_codec::ValueDecoder;
17use qubit_codec::ValueEncoder;
18
19use crate::MiscCodecError;
20use crate::MiscCodecResult;
21
22/// Encodes and decodes Base64 byte strings.
23///
24/// This facade intentionally remains a whole-value codec backed by the
25/// `base64` crate. Final partial quantum handling and optional `=` padding are
26/// facade/transcoder responsibilities, not part of the low-level quantum codec.
27///
28/// # Examples
29///
30/// ```
31/// use qubit_codec_misc::Base64Codec;
32///
33/// let codec = Base64Codec::standard();
34/// assert_eq!(codec.encode(b"hello"), "aGVsbG8=");
35/// ```
36#[must_use]
37#[derive(Debug, Clone, Copy)]
38pub struct Base64Codec {
39    /// Static engine that defines this codec's alphabet and padding policy.
40    engine: &'static GeneralPurpose,
41}
42
43impl PartialEq for Base64Codec {
44    #[inline]
45    fn eq(&self, other: &Self) -> bool {
46        core::ptr::eq(self.engine, other.engine)
47    }
48}
49
50impl Eq for Base64Codec {}
51
52impl Base64Codec {
53    /// Creates a standard Base64 codec with padding.
54    ///
55    /// # Returns
56    /// Standard Base64 codec.
57    #[inline]
58    pub fn standard() -> Self {
59        Self { engine: &STANDARD }
60    }
61
62    /// Creates a standard Base64 codec without padding.
63    ///
64    /// # Returns
65    /// Standard no-padding Base64 codec.
66    #[inline]
67    pub fn standard_no_pad() -> Self {
68        Self {
69            engine: &STANDARD_NO_PAD,
70        }
71    }
72
73    /// Creates a URL-safe Base64 codec with padding.
74    ///
75    /// # Returns
76    /// URL-safe Base64 codec.
77    #[inline]
78    pub fn url_safe() -> Self {
79        Self { engine: &URL_SAFE }
80    }
81
82    /// Creates a URL-safe Base64 codec without padding.
83    ///
84    /// # Returns
85    /// URL-safe no-padding Base64 codec.
86    #[inline]
87    pub fn url_safe_no_pad() -> Self {
88        Self {
89            engine: &URL_SAFE_NO_PAD,
90        }
91    }
92
93    /// Encodes bytes into Base64 text.
94    ///
95    /// # Parameters
96    /// - `bytes`: Bytes to encode.
97    ///
98    /// # Returns
99    /// Encoded Base64 text.
100    #[inline]
101    #[must_use]
102    pub fn encode(&self, bytes: &[u8]) -> String {
103        self.engine.encode(bytes)
104    }
105
106    /// Decodes Base64 text into bytes.
107    ///
108    /// # Parameters
109    /// - `text`: Base64 text.
110    ///
111    /// # Returns
112    /// Decoded bytes.
113    ///
114    /// # Errors
115    /// Returns [`MiscCodecError::InvalidBase64`] when `text` is malformed.
116    #[inline]
117    pub fn decode(&self, text: &str) -> MiscCodecResult<Vec<u8>> {
118        self.engine.decode(text).map_err(MiscCodecError::from)
119    }
120}
121
122impl Default for Base64Codec {
123    /// Creates a standard Base64 codec with padding.
124    #[inline]
125    fn default() -> Self {
126        Self::standard()
127    }
128}
129
130impl ValueEncoder<[u8]> for Base64Codec {
131    type Error = MiscCodecError;
132    type Output = String;
133
134    /// Encodes bytes into Base64 text.
135    #[inline]
136    fn encode(&mut self, input: &[u8]) -> Result<Self::Output, Self::Error> {
137        Ok(Base64Codec::encode(self, input))
138    }
139}
140
141impl ValueDecoder<str> for Base64Codec {
142    type Error = MiscCodecError;
143    type Output = Vec<u8>;
144
145    /// Decodes Base64 text into bytes.
146    #[inline]
147    fn decode(&mut self, input: &str) -> Result<Self::Output, Self::Error> {
148        Base64Codec::decode(self, input)
149    }
150}