multi_base/base.rs
1// SPDX-License-Identifier: MIT
2
3use crate::impls::BaseCodec;
4
5#[cfg(not(feature = "std"))]
6use alloc::{string::String, vec::Vec};
7
8/// Generates the `Base` enum and its implementation with encode/decode methods.
9///
10/// This macro creates a comprehensive base encoding enum with:
11/// - Enum variants for each base type with documentation
12/// - `from_code()` method to convert character codes to base types
13/// - `code()` method to get the character code for a base
14/// - `encode()` method to encode bytes to a base string
15/// - `decode()` method to decode base strings back to bytes
16///
17/// # Macro Hygiene
18///
19/// This macro uses `$crate::` prefixes to ensure proper hygiene when invoked
20/// from other modules or crates.
21///
22/// # Parameters
23///
24/// The macro accepts a comma-separated list of base definitions in the form:
25/// ```text
26/// #[doc = "Documentation"] 'code' => VariantName,
27/// ```
28///
29/// Where:
30/// - `#[doc = "..."]` - Documentation attribute for the enum variant
31/// - `'code'` - The single character or emoji that identifies this base encoding
32/// - `VariantName` - The `PascalCase` name for the enum variant
33///
34/// # Generated Code
35///
36/// For each base definition, the macro generates:
37/// - An enum variant in the `Base` enum
38/// - A match arm in `from_code()` that maps the character code to the variant
39/// - A match arm in `code()` that returns the character code
40/// - A match arm in `encode()` that delegates to the base's encode function
41/// - A match arm in `decode()` that delegates to the base's decode function
42///
43/// # Example
44///
45/// ```ignore
46/// build_base_enum! {
47/// /// Base64 encoding
48/// 'm' => Base64,
49/// /// Base58 Bitcoin encoding
50/// 'z' => Base58Btc,
51/// }
52/// ```
53///
54/// This generates a `Base` enum with `Base64` and `Base58Btc` variants,
55/// and implements all required methods.
56///
57/// # Error Handling
58///
59/// The `from_code()` method returns `Result<Base>` and will return
60/// `Error::UnknownBase` if given an unrecognized character code.
61macro_rules! build_base_enum {
62 ( $(#[$attr:meta] $code:expr => $base:ident,)* ) => {
63 /// List of types currently supported in the multibase spec.
64 ///
65 /// Not all base types are supported by this library.
66 #[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]
67 pub enum Base {
68 $( #[$attr] $base, )*
69 }
70
71 impl Base {
72 /// Convert a character code to the matching base algorithm.
73 ///
74 /// Returns `Error::UnknownBase` if the code doesn't match any supported base.
75 ///
76 /// # Errors
77 ///
78 /// Returns [`Error::UnknownBase`](crate::error::Error::UnknownBase) if `code`
79 /// does not correspond to any supported base encoding.
80 #[inline]
81 pub const fn from_code(code: char) -> $crate::error::Result<Self> {
82 match code {
83 $( $code => Ok(Self::$base), )*
84 _ => Err($crate::error::Error::UnknownBase { code }),
85 }
86 }
87
88 /// Get the character code corresponding to the base algorithm.
89 ///
90 /// Each base encoding has a unique single-character (or emoji) prefix
91 /// that identifies it in multibase strings.
92 #[inline]
93 pub const fn code(&self) -> char {
94 match self {
95 $( Self::$base => $code, )*
96 }
97 }
98
99 /// Encode the given byte slice to a base string (without prefix).
100 ///
101 /// This method returns only the encoded data, without the base code prefix.
102 /// Use the public `encode()` function to get the full multibase string.
103 #[inline]
104 pub fn encode<I: AsRef<[u8]>>(&self, input: I) -> String {
105 match self {
106 $( Self::$base => $crate::impls::$base::encode(input), )*
107 }
108 }
109
110 /// Decode the base string (without prefix) back to bytes.
111 ///
112 /// The `strict` parameter controls whether to accept case-insensitive
113 /// input for applicable bases.
114 ///
115 /// # Parameters
116 ///
117 /// * `input` - The encoded string (without the multibase prefix)
118 /// * `strict` - If true, enforce strict decoding rules (case-sensitive, etc.)
119 ///
120 /// # Errors
121 ///
122 /// Returns an error if the input contains invalid characters for this base
123 /// or if the input is malformed.
124 #[inline]
125 pub fn decode<I: AsRef<str>>(&self, input: I, strict: bool) -> $crate::error::Result<Vec<u8>> {
126 match self {
127 $( Self::$base => $crate::impls::$base::decode(input, strict), )*
128 }
129 }
130
131 #[inline]
132 pub(crate) fn decode_into<I: AsRef<str>>(&self, input: I, strict: bool, buffer: &mut Vec<u8>) -> $crate::error::Result<()> {
133 match self {
134 $( Self::$base => $crate::impls::$base::decode_into(input, strict, buffer), )*
135 }
136 }
137 }
138 }
139}
140
141build_base_enum! {
142 /// 8-bit binary (encoder and decoder keeps data unmodified).
143 '\x00' => Identity,
144 /// Base2 (alphabet: 01).
145 '0' => Base2,
146 /// Base8 (alphabet: 01234567).
147 '7' => Base8,
148 /// Base10 (alphabet: 0123456789).
149 '9' => Base10,
150 /// Base16 lower hexadecimal (alphabet: 0123456789abcdef).
151 'f' => Base16Lower,
152 /// Base16 upper hexadecimal (alphabet: 0123456789ABCDEF).
153 'F' => Base16Upper,
154 /// Base32, rfc4648 no padding (alphabet: abcdefghijklmnopqrstuvwxyz234567).
155 'b' => Base32Lower,
156 /// Base32, rfc4648 no padding (alphabet: ABCDEFGHIJKLMNOPQRSTUVWXYZ234567).
157 'B' => Base32Upper,
158 /// Base32, rfc4648 with padding (alphabet: abcdefghijklmnopqrstuvwxyz234567).
159 'c' => Base32PadLower,
160 /// Base32, rfc4648 with padding (alphabet: ABCDEFGHIJKLMNOPQRSTUVWXYZ234567).
161 'C' => Base32PadUpper,
162 /// Base32hex, rfc4648 no padding (alphabet: 0123456789abcdefghijklmnopqrstuv).
163 'v' => Base32HexLower,
164 /// Base32hex, rfc4648 no padding (alphabet: 0123456789ABCDEFGHIJKLMNOPQRSTUV).
165 'V' => Base32HexUpper,
166 /// Base32hex, rfc4648 with padding (alphabet: 0123456789abcdefghijklmnopqrstuv).
167 't' => Base32HexPadLower,
168 /// Base32hex, rfc4648 with padding (alphabet: 0123456789ABCDEFGHIJKLMNOPQRSTUV).
169 'T' => Base32HexPadUpper,
170 /// z-base-32 (used by Tahoe-LAFS) (alphabet: ybndrfg8ejkmcpqxot1uwisza345h769).
171 'h' => Base32Z,
172 /// Base36, [0-9a-z] no padding (alphabet: 0123456789abcdefghijklmnopqrstuvwxyz).
173 'k' => Base36Lower,
174 /// Base36, [0-9A-Z] no padding (alphabet: 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ).
175 'K' => Base36Upper,
176 /// Base58 flicker (alphabet: 123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ).
177 'Z' => Base58Flickr,
178 /// Base58 bitcoin (alphabet: 123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz).
179 'z' => Base58Btc,
180 /// Base64, `rfc4648` no padding (alphabet: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/).
181 'm' => Base64,
182 /// Base64, `rfc4648` with padding (alphabet: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/).
183 'M' => Base64Pad,
184 /// Base64 url, `rfc4648` no padding (alphabet: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_).
185 'u' => Base64Url,
186 /// Base64 url, `rfc4648` with padding (alphabet: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_).
187 'U' => Base64UrlPad,
188 /// Base256Emoji (alphabet: ๐๐ชโ๐ฐ๐๐๐๐๐๐๐๐๐๐๐๐๐โ๐ป๐ฅ๐พ๐ฟ๐โค๐๐คฃ๐๐๐๐ญ๐๐๐
๐๐๐ฅ๐ฅฐ๐๐๐๐ข๐ค๐๐๐ช๐โบ๐๐ค๐๐๐๐๐น๐คฆ๐๐โโจ๐คท๐ฑ๐๐ธ๐๐๐๐๐๐๐๐๐คฉ๐๐๐ค๐๐ฏ๐๐๐ถ๐๐คญโฃ๐๐๐๐ช๐๐ฅ๐๐๐ฉ๐ก๐คช๐๐ฅณ๐ฅ๐คค๐๐๐ณโ๐๐๐ด๐๐ฌ๐๐๐ท๐ป๐โญโ
๐ฅบ๐๐๐ค๐ฆโ๐ฃ๐๐โน๐๐๐ โ๐๐บ๐๐ป๐๐๐๐๐น๐ฃ๐ซ๐๐๐ต๐ค๐๐ด๐ค๐ผ๐ซโฝ๐คโ๐๐คซ๐๐ฎ๐๐ป๐๐ถ๐๐ฒ๐ฟ๐งก๐โก๐๐โโ๐๐ฐ๐คจ๐ถ๐ค๐ถ๐ฐ๐๐ข๐ค๐๐จ๐จ๐คฌโ๐๐บ๐ค๐๐๐ฑ๐๐ถ๐ฅดโถโกโ๐๐ธโฌ๐จ๐๐ฆ๐ท๐บโ ๐
๐๐ต๐๐คฒ๐ค ๐คง๐๐ต๐
๐ง๐พ๐๐๐ค๐๐คฏ๐ทโ๐ง๐ฏ๐๐๐ค๐๐โ๐ด๐ฃ๐ธ๐๐๐ฅ๐คข๐
๐ก๐ฉ๐๐ธ๐ป๐ค๐คฎ๐ผ๐ฅต๐ฉ๐๐๐ผ๐๐ฃ๐ฅ)
189 '๐' => Base256Emoji,
190}