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().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}