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