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#[must_use]
28#[derive(Debug, Clone, Copy)]
29pub struct Base64Codec {
30    engine: &'static GeneralPurpose,
31}
32
33impl PartialEq for Base64Codec {
34    #[inline]
35    fn eq(&self, other: &Self) -> bool {
36        core::ptr::eq(self.engine, other.engine)
37    }
38}
39
40impl Eq for Base64Codec {}
41
42impl Base64Codec {
43    /// Creates a standard Base64 codec with padding.
44    ///
45    /// # Returns
46    /// Standard Base64 codec.
47    #[inline]
48    pub fn standard() -> Self {
49        Self { engine: &STANDARD }
50    }
51
52    /// Creates a standard Base64 codec without padding.
53    ///
54    /// # Returns
55    /// Standard no-padding Base64 codec.
56    #[inline]
57    pub fn standard_no_pad() -> Self {
58        Self {
59            engine: &STANDARD_NO_PAD,
60        }
61    }
62
63    /// Creates a URL-safe Base64 codec with padding.
64    ///
65    /// # Returns
66    /// URL-safe Base64 codec.
67    #[inline]
68    pub fn url_safe() -> Self {
69        Self { engine: &URL_SAFE }
70    }
71
72    /// Creates a URL-safe Base64 codec without padding.
73    ///
74    /// # Returns
75    /// URL-safe no-padding Base64 codec.
76    #[inline]
77    pub fn url_safe_no_pad() -> Self {
78        Self {
79            engine: &URL_SAFE_NO_PAD,
80        }
81    }
82
83    /// Encodes bytes into Base64 text.
84    ///
85    /// # Parameters
86    /// - `bytes`: Bytes to encode.
87    ///
88    /// # Returns
89    /// Encoded Base64 text.
90    #[inline]
91    #[must_use]
92    pub fn encode(&self, bytes: &[u8]) -> String {
93        self.engine.encode(bytes)
94    }
95
96    /// Decodes Base64 text into bytes.
97    ///
98    /// # Parameters
99    /// - `text`: Base64 text.
100    ///
101    /// # Returns
102    /// Decoded bytes.
103    ///
104    /// # Errors
105    /// Returns [`MiscCodecError::InvalidBase64`] when `text` is malformed.
106    #[inline]
107    pub fn decode(&self, text: &str) -> MiscCodecResult<Vec<u8>> {
108        self.engine.decode(text).map_err(MiscCodecError::from)
109    }
110}
111
112impl Default for Base64Codec {
113    /// Creates a standard Base64 codec with padding.
114    #[inline]
115    fn default() -> Self {
116        Self::standard()
117    }
118}
119
120impl ValueEncoder<[u8]> for Base64Codec {
121    type Error = MiscCodecError;
122    type Output = String;
123
124    /// Encodes bytes into Base64 text.
125    #[inline(always)]
126    fn encode(&mut self, input: &[u8]) -> Result<Self::Output, Self::Error> {
127        Ok(Base64Codec::encode(self, input))
128    }
129}
130
131impl ValueDecoder<str> for Base64Codec {
132    type Error = MiscCodecError;
133    type Output = Vec<u8>;
134
135    /// Decodes Base64 text into bytes.
136    #[inline(always)]
137    fn decode(&mut self, input: &str) -> Result<Self::Output, Self::Error> {
138        Base64Codec::decode(self, input)
139    }
140}