Skip to main content

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}