qubit_codec/base64_codec.rs
1/*******************************************************************************
2 *
3 * Copyright (c) 2026 Haixing Hu.
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10//! Base64 byte codec.
11
12use ::base64::Engine;
13use ::base64::engine::general_purpose::{
14 STANDARD,
15 STANDARD_NO_PAD,
16 URL_SAFE,
17 URL_SAFE_NO_PAD,
18};
19
20use crate::{
21 CodecError,
22 CodecResult,
23 Decoder,
24 Encoder,
25};
26
27/// Encodes and decodes Base64 byte strings.
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub struct Base64Codec {
30 url_safe: bool,
31 padding: bool,
32}
33
34impl Base64Codec {
35 /// Creates a standard Base64 codec with padding.
36 ///
37 /// # Returns
38 /// Standard Base64 codec.
39 pub fn standard() -> Self {
40 Self {
41 url_safe: false,
42 padding: true,
43 }
44 }
45
46 /// Creates a standard Base64 codec without padding.
47 ///
48 /// # Returns
49 /// Standard no-padding Base64 codec.
50 pub fn standard_no_pad() -> Self {
51 Self {
52 url_safe: false,
53 padding: false,
54 }
55 }
56
57 /// Creates a URL-safe Base64 codec with padding.
58 ///
59 /// # Returns
60 /// URL-safe Base64 codec.
61 pub fn url_safe() -> Self {
62 Self {
63 url_safe: true,
64 padding: true,
65 }
66 }
67
68 /// Creates a URL-safe Base64 codec without padding.
69 ///
70 /// # Returns
71 /// URL-safe no-padding Base64 codec.
72 pub fn url_safe_no_pad() -> Self {
73 Self {
74 url_safe: true,
75 padding: false,
76 }
77 }
78
79 /// Encodes bytes into Base64 text.
80 ///
81 /// # Parameters
82 /// - `bytes`: Bytes to encode.
83 ///
84 /// # Returns
85 /// Encoded Base64 text.
86 pub fn encode(&self, bytes: &[u8]) -> String {
87 self.engine().encode(bytes)
88 }
89
90 /// Decodes Base64 text into bytes.
91 ///
92 /// # Parameters
93 /// - `text`: Base64 text.
94 ///
95 /// # Returns
96 /// Decoded bytes.
97 ///
98 /// # Errors
99 /// Returns [`CodecError::InvalidInput`] when `text` is malformed.
100 pub fn decode(&self, text: &str) -> CodecResult<Vec<u8>> {
101 self.engine().decode(text).map_err(|source| CodecError::InvalidInput {
102 codec: "base64",
103 reason: source.to_string(),
104 })
105 }
106
107 /// Selects the concrete Base64 engine.
108 ///
109 /// # Returns
110 /// Base64 engine matching this codec's alphabet and padding settings.
111 fn engine(&self) -> &'static ::base64::engine::GeneralPurpose {
112 match (self.url_safe, self.padding) {
113 (false, true) => &STANDARD,
114 (false, false) => &STANDARD_NO_PAD,
115 (true, true) => &URL_SAFE,
116 (true, false) => &URL_SAFE_NO_PAD,
117 }
118 }
119}
120
121impl Default for Base64Codec {
122 /// Creates a standard Base64 codec with padding.
123 fn default() -> Self {
124 Self::standard()
125 }
126}
127
128impl Encoder<[u8]> for Base64Codec {
129 type Error = CodecError;
130 type Output = String;
131
132 /// Encodes bytes into Base64 text.
133 fn encode(&self, input: &[u8]) -> Result<Self::Output, Self::Error> {
134 Ok(Base64Codec::encode(self, input))
135 }
136}
137
138impl Decoder<str> for Base64Codec {
139 type Error = CodecError;
140 type Output = Vec<u8>;
141
142 /// Decodes Base64 text into bytes.
143 fn decode(&self, input: &str) -> Result<Self::Output, Self::Error> {
144 Base64Codec::decode(self, input)
145 }
146}