use base64::{Engine as _, engine::general_purpose};
use urlencoding;
pub struct Base64Util;
impl Base64Util {
pub fn encode(data: &[u8]) -> String {
general_purpose::STANDARD.encode(data)
}
pub fn decode(data: &str) -> Result<Vec<u8>, base64::DecodeError> {
general_purpose::STANDARD.decode(data)
}
pub fn encode_str(data: &str) -> String {
Self::encode(data.as_bytes())
}
pub fn decode_str(data: &str) -> Result<String, Box<dyn std::error::Error>> {
let bytes = Self::decode(data)?;
Ok(String::from_utf8(bytes)?)
}
}
pub struct Base58Util;
impl Base58Util {
const ALPHABET: &'static [u8] = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
pub fn encode(data: &[u8]) -> String {
if data.is_empty() {
return String::new();
}
let mut result = Vec::new();
let mut num = data.iter().fold(0u128, |acc, &b| (acc << 8) | b as u128);
while num > 0 {
let remainder = (num % 58) as usize;
result.push(Self::ALPHABET[remainder]);
num /= 58;
}
for &byte in data {
if byte == 0 {
result.push(b'1');
} else {
break;
}
}
result.reverse();
String::from_utf8(result).unwrap()
}
pub fn decode(data: &str) -> Result<Vec<u8>, &'static str> {
if data.is_empty() {
return Ok(Vec::new());
}
let mut result = 0u128;
let mut leading_zeros = 0;
for &byte in data.as_bytes() {
if byte == b'1' {
leading_zeros += 1;
} else {
break;
}
}
for &byte in data.as_bytes() {
let digit = Self::ALPHABET.iter().position(|&c| c == byte)
.ok_or("Invalid Base58 character")?;
result = result.checked_mul(58).ok_or("Number too large")?;
result = result.checked_add(digit as u128).ok_or("Number too large")?;
}
let mut bytes = Vec::new();
let mut temp = result;
while temp > 0 {
bytes.push((temp % 256) as u8);
temp /= 256;
}
bytes.reverse();
let mut final_result = vec![0u8; leading_zeros];
final_result.extend(bytes);
Ok(final_result)
}
}
pub struct HexUtil;
impl HexUtil {
pub fn encode(data: &[u8]) -> String {
data.iter()
.map(|b| format!("{:02x}", b))
.collect()
}
pub fn encode_upper(data: &[u8]) -> String {
data.iter()
.map(|b| format!("{:02X}", b))
.collect()
}
pub fn decode(data: &str) -> Result<Vec<u8>, &'static str> {
if data.len() % 2 != 0 {
return Err("Hex string length must be even");
}
(0..data.len())
.step_by(2)
.map(|i| u8::from_str_radix(&data[i..i + 2], 16).map_err(|_| "Invalid hex character"))
.collect()
}
pub fn is_valid_hex(data: &str) -> bool {
data.len() % 2 == 0 && data.chars().all(|c| c.is_ascii_hexdigit())
}
}
pub struct UrlUtil;
impl UrlUtil {
pub fn encode(data: &str) -> String {
urlencoding::encode(data).to_string()
}
pub fn decode(data: &str) -> Result<String, std::string::FromUtf8Error> {
urlencoding::decode(data).map(|cow| cow.to_string()).map_err(|e| e)
}
}
pub struct PercentUtil;
impl PercentUtil {
pub fn encode(data: &str) -> String {
let mut result = String::new();
for byte in data.as_bytes() {
match byte {
b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' => {
result.push(*byte as char);
}
_ => {
result.push_str(&format!("%{:02X}", byte));
}
}
}
result
}
pub fn decode(data: &str) -> Result<String, &'static str> {
let mut result = Vec::new();
let bytes = data.as_bytes();
let mut i = 0;
while i < bytes.len() {
match bytes[i] {
b'%' => {
if i + 2 >= bytes.len() {
return Err("Invalid percent encoding");
}
let hex = &data[i + 1..i + 3];
let byte = u8::from_str_radix(hex, 16).map_err(|_| "Invalid hex in percent encoding")?;
result.push(byte);
i += 3;
}
b'+' => {
result.push(b' ');
i += 1;
}
_ => {
result.push(bytes[i]);
i += 1;
}
}
}
String::from_utf8(result).map_err(|_| "Invalid UTF-8 in percent encoding")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_base64_encode_decode() {
let original = "Hello, World!";
let encoded = Base64Util::encode_str(original);
let decoded = Base64Util::decode_str(&encoded).unwrap();
assert_eq!(decoded, original);
}
#[test]
fn test_base58_encode_decode() {
let original = "Hello";
let encoded = Base58Util::encode(original.as_bytes());
let decoded = Base58Util::decode(&encoded).unwrap();
assert_eq!(String::from_utf8(decoded).unwrap(), original);
}
#[test]
fn test_hex_encode_decode() {
let original = "Hello";
let encoded = HexUtil::encode(original.as_bytes());
let decoded = HexUtil::decode(&encoded).unwrap();
assert_eq!(String::from_utf8(decoded).unwrap(), original);
}
#[test]
fn test_hex_upper_encode() {
let original = "Hello";
let encoded = HexUtil::encode_upper(original.as_bytes());
assert_eq!(encoded, "48656C6C6F");
}
#[test]
fn test_hex_validity() {
assert!(HexUtil::is_valid_hex("48656c6c6f"));
assert!(!HexUtil::is_valid_hex("48656c6c6g"));
assert!(!HexUtil::is_valid_hex("48656c6c6"));
}
#[test]
fn test_url_encode_decode() {
let original = "Hello World!";
let encoded = UrlUtil::encode(original);
let decoded = UrlUtil::decode(&encoded).unwrap();
assert_eq!(decoded, original);
}
#[test]
fn test_percent_encode_decode() {
let original = "Hello World!";
let encoded = PercentUtil::encode(original);
let decoded = PercentUtil::decode(&encoded).unwrap();
assert_eq!(decoded, original);
}
#[test]
fn test_percent_encode_special_chars() {
let original = "你好世界";
let encoded = PercentUtil::encode(original);
assert!(encoded.contains('%'));
let decoded = PercentUtil::decode(&encoded).unwrap();
assert_eq!(decoded, original);
}
}