Skip to main content

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}