use base64::{Engine, engine::general_purpose};
pub fn base64url_encode(data: &[u8]) -> String {
let encoded = general_purpose::STANDARD.encode(data);
encoded
.replace('+', "-")
.replace('/', "_")
.trim_end_matches('=')
.to_string()
}
pub fn base64url_decode(s: &str) -> Result<Vec<u8>, base64::DecodeError> {
let standard = s.replace('-', "+").replace('_', "/");
let padding = (4 - (standard.len() % 4)) % 4;
let padded = format!("{}{}", standard, "=".repeat(padding));
general_purpose::STANDARD.decode(&padded)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_roundtrip() {
let original = b"Hello, MEGA!";
let encoded = base64url_encode(original);
let decoded = base64url_decode(&encoded).unwrap();
assert_eq!(decoded, original);
}
#[test]
fn test_no_padding() {
let encoded = base64url_encode(b"test");
assert!(!encoded.contains('='));
}
#[test]
fn test_url_safe_chars() {
let data: Vec<u8> = (0..255).collect();
let encoded = base64url_encode(&data);
assert!(!encoded.contains('+'));
assert!(!encoded.contains('/'));
}
#[test]
fn test_decode_with_url_safe_chars() {
let encoded = "SGVsbG8tV29ybGRf"; let decoded = base64url_decode(encoded);
assert!(decoded.is_ok());
}
}