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()
102 .decode(text)
103 .map_err(|source| CodecError::InvalidInput {
104 codec: "base64",
105 reason: source.to_string(),
106 })
107 }
108
109 /// Selects the concrete Base64 engine.
110 ///
111 /// # Returns
112 /// Base64 engine matching this codec's alphabet and padding settings.
113 fn engine(&self) -> &'static ::base64::engine::GeneralPurpose {
114 match (self.url_safe, self.padding) {
115 (false, true) => &STANDARD,
116 (false, false) => &STANDARD_NO_PAD,
117 (true, true) => &URL_SAFE,
118 (true, false) => &URL_SAFE_NO_PAD,
119 }
120 }
121}
122
123impl Default for Base64Codec {
124 /// Creates a standard Base64 codec with padding.
125 fn default() -> Self {
126 Self::standard()
127 }
128}
129
130impl Encoder<[u8]> for Base64Codec {
131 type Error = CodecError;
132 type Output = String;
133
134 /// Encodes bytes into Base64 text.
135 fn encode(&self, input: &[u8]) -> Result<Self::Output, Self::Error> {
136 Ok(Base64Codec::encode(self, input))
137 }
138}
139
140impl Decoder<str> for Base64Codec {
141 type Error = CodecError;
142 type Output = Vec<u8>;
143
144 /// Decodes Base64 text into bytes.
145 fn decode(&self, input: &str) -> Result<Self::Output, Self::Error> {
146 Base64Codec::decode(self, input)
147 }
148}