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}