diff --git a/Cargo.lock b/Cargo.lock index c53f517..14fe937 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -115,6 +115,12 @@ dependencies = [ "match-lookup", ] +[[package]] +name = "base45" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3103f6d6af63726e86b026ac66769fc578ea38aba33f409e5855a4def97a564" + [[package]] name = "bitflags" version = "1.3.2" @@ -479,6 +485,7 @@ version = "0.9.2" dependencies = [ "base-x", "base256emoji", + "base45", "criterion", "data-encoding", "data-encoding-macro", diff --git a/Cargo.toml b/Cargo.toml index 131513a..47cda72 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ base-x = { version = "0.2.7", default-features = false } base256emoji = "1.0.2" data-encoding = { version = "2.3.1", default-features = false, features = ["alloc"] } data-encoding-macro = "0.1.9" +base45 = "3.1.0" [dev-dependencies] criterion = "0.7" diff --git a/cli/main.rs b/cli/main.rs index 529b6d0..ec11468 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -84,6 +84,7 @@ impl fmt::Display for StrBase { Base::Base32Z => "base32z", Base::Base36Lower => "base36lower", Base::Base36Upper => "base36upper", + Base::Base45 => "base45", Base::Base58Flickr => "base58flickr", Base::Base58Btc => "base58btc", Base::Base64 => "base64", @@ -118,6 +119,7 @@ impl FromStr for StrBase { "base32z" => Ok(Base::Base32Z), "base36lower" => Ok(Base::Base36Lower), "base36upper" => Ok(Base::Base36Upper), + "base45" => Ok(Base::Base45), "base58flickr" => Ok(Base::Base58Flickr), "base58btc" => Ok(Base::Base58Btc), "base64" => Ok(Base::Base64), diff --git a/src/base.rs b/src/base.rs index 40b55ab..470fc65 100644 --- a/src/base.rs +++ b/src/base.rs @@ -82,6 +82,8 @@ build_base_enum! { 'k' => Base36Lower, /// Base36, [0-9A-Z] no padding (alphabet: 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ). 'K' => Base36Upper, + /// Base45, rfc9285 (alphabet: 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:). + 'R' => Base45, /// Base58 flicker (alphabet: 123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ). 'Z' => Base58Flickr, /// Base58 bitcoin (alphabet: 123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz). diff --git a/src/error.rs b/src/error.rs index 5fb14a6..790296c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -41,3 +41,9 @@ impl From for Error { Self::InvalidBaseString } } + +impl From for Error { + fn from(_: base45::DecodeError) -> Self { + Self::InvalidBaseString + } +} diff --git a/src/impls.rs b/src/impls.rs index fbc915a..a8d255e 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -158,3 +158,17 @@ impl BaseCodec for Base36Upper { Ok(base_x::decode(encoding::BASE36_UPPER, &uppercased)?) } } + +/// Base45, rfc9285 (alphabet: 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:). +pub(crate) struct Base45; + +impl BaseCodec for Base45 { + fn encode>(input: I) -> String { + base45::encode(input.as_ref()) + } + + fn decode>(input: I) -> Result> { + let uppercased = input.as_ref().to_ascii_uppercase(); + Ok(base45::decode(&uppercased)?) + } +} diff --git a/tests/lib.rs b/tests/lib.rs index e0a7264..b222be0 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -58,6 +58,7 @@ fn test_basic() { (Base32Z, "hxf1zgedpcfzg1ebb"), (Base36Lower, "k2lcpzo5yikidynfl"), (Base36Upper, "K2LCPZO5YIKIDYNFL"), + (Base45, "RRFF.OEB$D5/DZ24"), (Base58Flickr, "Z7Pznk19XTTzBtx"), (Base58Btc, "z7paNL19xttacUY"), (Base64, "meWVzIG1hbmkgIQ"), @@ -90,6 +91,7 @@ fn preserves_leading_zero() { (Base32Z, "hybhskh3ypiosh4jyrr"), (Base36Lower, "k02lcpzo5yikidynfl"), (Base36Upper, "K02LCPZO5YIKIDYNFL"), + (Base45, "RV206$CL44CEC2DDX0"), (Base58Flickr, "Z17Pznk19XTTzBtx"), (Base58Btc, "z17paNL19xttacUY"), (Base64, "mAHllcyBtYW5pICE"), @@ -122,6 +124,7 @@ fn preserves_two_leading_zeroes() { (Base32Z, "hyyy813murbssn5ujryoo"), (Base36Lower, "k002lcpzo5yikidynfl"), (Base36Upper, "K002LCPZO5YIKIDYNFL"), + (Base45, "R000RFF.OEB$D5/DZ24"), (Base58Flickr, "Z117Pznk19XTTzBtx"), (Base58Btc, "z117paNL19xttacUY"), (Base64, "mAAB5ZXMgbWFuaSAh"), @@ -149,6 +152,7 @@ fn case_insensitivity() { (Base32HexPadUpper, "Td1imor3f41RMUSJCCG======"), (Base36Lower, "kfUvrsIvVnfRbjWaJo"), (Base36Upper, "KfUVrSIVVnFRbJWAJo"), + (Base45, "R+8d vd82ek4f.kea2"), ]; for (base, output) in test_cases { assert_eq!(decode(output).unwrap(), (base, input.to_vec()));