Skip to main content

base_x/
lib.rs

1//! # base_x
2//!
3//! Encode and decode any base alphabet.
4//!
5//! ## Installation
6//!
7//! Add this to `Cargo.toml` file:
8//!
9//! ```toml
10//! [dependencies]
11//! base-x = "0.2.0"
12//! ```
13//!
14//! ## Usage
15//!
16//! ```rust
17//! fn main() {
18//!   let decoded = base_x::decode("01", "11111111000000001111111100000000").unwrap();
19//!   let encoded = base_x::encode("01", &decoded);
20//!  assert_eq!(encoded, "11111111000000001111111100000000");
21//! }
22//! ```
23
24#![cfg_attr(not(feature = "std"), no_std)]
25
26#[cfg(not(feature = "std"))]
27extern crate alloc;
28
29pub mod alphabet;
30mod bigint;
31pub mod decoder;
32pub mod encoder;
33
34pub use alphabet::Alphabet;
35
36#[cfg(not(feature = "std"))]
37use alloc::{string::String, vec::Vec};
38
39#[cfg(not(feature = "std"))]
40use core as std;
41
42use std::fmt;
43
44#[derive(Debug)]
45pub struct DecodeError;
46
47impl fmt::Display for DecodeError {
48    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
49        write!(f, "Failed to decode the given data")
50    }
51}
52
53#[cfg(feature = "std")]
54impl std::error::Error for DecodeError {}
55
56/// Encode an input vector using the given alphabet.
57pub fn encode<A: Alphabet>(alphabet: A, input: &[u8]) -> String {
58    alphabet.encode(input)
59}
60
61/// Decode an input vector using the given alphabet.
62pub fn decode<A: Alphabet>(alphabet: A, input: &str) -> Result<Vec<u8>, DecodeError> {
63    alphabet.decode(input)
64}
65
66#[cfg(test)]
67mod test {
68    use super::decode;
69    use super::encode;
70    use std::fs::File;
71    use std::io::Read;
72
73    #[test]
74    fn works() {
75        let mut file = File::open("./fixtures/fixtures.json").unwrap();
76        let mut data = String::new();
77        file.read_to_string(&mut data).unwrap();
78
79        let json: serde_json::Value = serde_json::from_str(&data).unwrap();
80        let alphabets = &json["alphabets"];
81
82        for value in json["valid"].as_array().unwrap() {
83            let alphabet_name = value["alphabet"].as_str().unwrap();
84            let input = value["string"].as_str().unwrap();
85            let alphabet = alphabets[alphabet_name].as_str().unwrap();
86
87            // Alphabet works as unicode
88            let decoded = decode(alphabet, input).unwrap();
89            let encoded = encode(alphabet, &decoded);
90            assert_eq!(encoded, input);
91
92            // Alphabet works as ASCII
93            let decoded = decode(alphabet.as_bytes(), input).unwrap();
94            let encoded = encode(alphabet.as_bytes(), &decoded);
95            assert_eq!(encoded, input);
96        }
97    }
98
99    #[test]
100    fn is_unicode_sound() {
101        // binary, kinda...
102        let alphabet = "😐😀";
103
104        let encoded = encode(alphabet, &[0xff, 0x00, 0xff, 0x00]);
105        let decoded = decode(alphabet, &encoded).unwrap();
106
107        assert_eq!(
108            encoded,
109            "😀😀😀😀😀😀😀😀😐😐😐😐😐😐😐😐😀😀😀😀😀😀😀😀😐😐😐😐😐😐😐😐"
110        );
111        assert_eq!(decoded, &[0xff, 0x00, 0xff, 0x00]);
112    }
113
114    #[test]
115    #[should_panic(expected = "at least 2")]
116    fn encode_empty_alphabet_panics() {
117        encode("", &[1u8]);
118    }
119
120    #[test]
121    #[should_panic(expected = "at least 2")]
122    fn encode_empty_alphabet_bytes_panics() {
123        encode(b"".as_slice(), &[1u8]);
124    }
125
126    #[test]
127    #[should_panic(expected = "at least 2")]
128    fn encode_single_char_alphabet_panics() {
129        encode("x", &[1u8]);
130    }
131
132    #[test]
133    #[should_panic(expected = "at least 2")]
134    fn encode_single_char_alphabet_bytes_panics() {
135        encode(b"x".as_slice(), &[1u8]);
136    }
137
138    #[test]
139    #[should_panic(expected = "duplicate")]
140    fn encode_duplicate_alphabet_panics() {
141        encode("aab", &[3u8]);
142    }
143
144    #[test]
145    #[should_panic(expected = "duplicate")]
146    fn encode_duplicate_alphabet_bytes_panics() {
147        encode(b"aab".as_slice(), &[3u8]);
148    }
149
150    #[test]
151    fn decode_empty_alphabet_returns_error() {
152        assert!(decode("", "abc").is_err());
153    }
154
155    #[test]
156    #[should_panic(expected = "duplicate")]
157    fn decode_duplicate_alphabet_panics() {
158        let _ = decode("aab", "aa");
159    }
160
161    #[test]
162    #[should_panic(expected = "duplicate")]
163    fn decode_duplicate_alphabet_bytes_panics() {
164        let _ = decode(b"aab".as_slice(), "aa");
165    }
166}