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