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}