url_cleaner_engine/glue/
base64.rs

1//! Glue for [`base64`].
2
3use serde::{Serialize, Deserialize};
4use base64::{engine::{general_purpose::GeneralPurpose, GeneralPurposeConfig, DecodePaddingMode}, alphabet::Alphabet};
5
6use crate::util::*;
7
8/// The config for how to encode and decode base64 text.
9/// # Examples
10/// ```
11/// use url_cleaner_engine::glue::*;
12///
13/// use ::base64::engine::Engine;
14/// let base64 = Base64Config::default().build();
15///
16/// let mut encoded = String::new();
17/// base64.encode_string("ab~d", &mut encoded);
18/// assert_eq!(encoded, "YWJ-ZA=="); // Note that - is used instead of +, because [`Base64Alphabet`] defaults to [`Base64Alphabet::UrlSafe`].
19///
20/// let mut decoded = Vec::new();
21/// base64.decode_vec(&encoded, &mut decoded).unwrap();
22/// assert_eq!(decoded, b"ab~d");
23/// ```
24#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Suitability)]
25#[serde(deny_unknown_fields)]
26pub struct Base64Config {
27    /// The alphabet to use.
28    ///
29    /// Defaults to [`Base64Alphabet::UrlSafe`].
30    #[serde(default, skip_serializing_if = "is_default")]
31    pub alphabet: Base64Alphabet,
32    /// If [`true`], encodes the `=` padding at the end.
33    ///
34    /// Defaults to [`true`].
35    #[serde(default = "get_true", skip_serializing_if = "is_true")]
36    pub encode_padding: bool,
37    /// Whether or not to require, refuse, or not care about padding when decoding.
38    ///
39    /// Defaults to [`DecodePaddingMode::Indifferent`].
40    #[serde(default, skip_serializing_if = "is_default")]
41    pub decode_padding: Base64DecodePaddingMode,
42    /// [`GeneralPurposeConfig::with_decode_allow_trailing_bits`].
43    #[serde(default, skip_serializing_if = "is_default")]
44    pub decode_allow_trailing_bits: bool
45}
46
47impl Base64Config {
48    /// Builds the [`GeneralPurpose`] base64 engine.
49    pub fn build(&self) -> GeneralPurpose {
50        GeneralPurpose::new(
51            self.alphabet.get(),
52            GeneralPurposeConfig::new()
53                .with_decode_padding_mode(self.decode_padding.build())
54                .with_encode_padding(self.encode_padding)
55                .with_decode_allow_trailing_bits(self.decode_allow_trailing_bits)
56        )
57    }
58}
59
60impl Default for Base64Config {
61    fn default() -> Self {
62        Self {
63            alphabet: Default::default(),
64            encode_padding: true,
65            decode_padding: Default::default(),
66            decode_allow_trailing_bits: false
67        }
68    }
69}
70
71/// The alphabet to use.
72///
73/// Defaults to [`Self::UrlSafe`], given this is a URL cleaning library.
74///
75/// See [Wikipedia's Base64 alphabet summary table](https://en.wikipedia.org/wiki/Base64#Variants_summary_table) for details.
76#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default, Suitability)]
77#[serde(deny_unknown_fields)]
78pub enum Base64Alphabet {
79    /// The standard alphabet, where characters 62 and 63 are `+` and `/`.
80    Standard,
81    /// The URL safe alphabet, where characters 62 and 63 are `-` and `_`.
82    #[default]
83    UrlSafe,
84    /// [`base64::alphabet::CRYPT`].
85    Crypt,
86    /// [`base64::alphabet::BCRYPT`].
87    Bcrypt,
88    /// [`base64::alphabet::IMAP_MUTF7`].
89    IMAPMUTF7,
90    /// [`base64::alphabet::BIN_HEX`].
91    BinHex
92}
93
94impl Base64Alphabet {
95    /// Makes a [`base64::alphabet::Alphabet`].
96    pub fn get(&self) -> &Alphabet {
97        match self {
98            Self::Standard  => &base64::alphabet::STANDARD,
99            Self::UrlSafe   => &base64::alphabet::URL_SAFE,
100            Self::Crypt     => &base64::alphabet::CRYPT,
101            Self::Bcrypt    => &base64::alphabet::BCRYPT,
102            Self::IMAPMUTF7 => &base64::alphabet::IMAP_MUTF7,
103            Self::BinHex    => &base64::alphabet::BIN_HEX
104        }
105    }
106}
107
108/// [`serde`] compatible [`DecodePaddingMode`].
109///
110/// Defaults to [`Self::Indifferent`].
111#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, Suitability)]
112#[serde(deny_unknown_fields)]
113pub enum Base64DecodePaddingMode {
114    /// Don't care whether or not the canonical padding is present.
115    ///
116    /// The default value.
117    #[default]
118    Indifferent,
119    /// Require that the canonical padding is present.
120    RequireCanonical,
121    /// Require that the canonical padding isn't present.
122    RequireNone
123}
124
125impl Base64DecodePaddingMode {
126    /// Builds a [`DecodePaddingMode`].
127    pub fn build(&self) -> DecodePaddingMode {
128        match self {
129            Self::Indifferent      => DecodePaddingMode::Indifferent,
130            Self::RequireCanonical => DecodePaddingMode::RequireCanonical,
131            Self::RequireNone      => DecodePaddingMode::RequireNone
132        }
133    }
134}