use crate::{ffi, mem};
pub fn base64_encode(data: &[u8]) -> String {
let (data_ptr, data_len) = mem::host_arg_bytes(data);
let result = unsafe { ffi::encoding_base64_encode(data_ptr, data_len) };
unsafe { mem::read_packed_string(result) }.unwrap_or_default()
}
pub fn base64_decode(input: &str) -> Option<Vec<u8>> {
let (data_ptr, data_len) = mem::host_arg_str(input);
let result = unsafe { ffi::encoding_base64_decode(data_ptr, data_len) };
unsafe { mem::read_packed_bytes(result) }
}
pub fn hex_encode(data: &[u8]) -> String {
let (data_ptr, data_len) = mem::host_arg_bytes(data);
let result = unsafe { ffi::encoding_hex_encode(data_ptr, data_len) };
unsafe { mem::read_packed_string(result) }.unwrap_or_default()
}
pub fn hex_decode(input: &str) -> Option<Vec<u8>> {
let (data_ptr, data_len) = mem::host_arg_str(input);
let result = unsafe { ffi::encoding_hex_decode(data_ptr, data_len) };
unsafe { mem::read_packed_bytes(result) }
}
pub fn url_encode(input: &str) -> String {
let (data_ptr, data_len) = mem::host_arg_str(input);
let result = unsafe { ffi::encoding_url_encode(data_ptr, data_len) };
unsafe { mem::read_packed_string(result) }.unwrap_or_default()
}
pub fn url_decode(input: &str) -> Option<String> {
let (data_ptr, data_len) = mem::host_arg_str(input);
let result = unsafe { ffi::encoding_url_decode(data_ptr, data_len) };
unsafe { mem::read_packed_string(result) }
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ffi::test_host;
#[test]
fn base64_encode_passes_input_and_returns_response() {
test_host::reset();
test_host::with_mock(|m| {
m.encoding_base64_encode_response = Some("aGVsbG8=".into());
});
assert_eq!(base64_encode(b"hello"), "aGVsbG8=");
assert_eq!(
test_host::read_mock(|m| m.last_encoding_base64_encode_input.clone()),
Some(b"hello".to_vec())
);
}
#[test]
fn base64_encode_empty_when_host_silent() {
test_host::reset();
assert_eq!(base64_encode(b"x"), "");
}
#[test]
fn base64_decode_returns_bytes() {
test_host::reset();
test_host::with_mock(|m| {
m.encoding_base64_decode_response = Some(b"hello".to_vec());
});
assert_eq!(base64_decode("aGVsbG8="), Some(b"hello".to_vec()));
}
#[test]
fn base64_decode_none_on_failure() {
test_host::reset();
assert!(base64_decode("not base64!").is_none());
}
#[test]
fn hex_encode_round_trip_via_mock() {
test_host::reset();
test_host::with_mock(|m| {
m.encoding_hex_encode_response = Some("deadbeef".into());
m.encoding_hex_decode_response = Some(vec![0xde, 0xad, 0xbe, 0xef]);
});
let encoded = hex_encode(&[0xde, 0xad, 0xbe, 0xef]);
assert_eq!(encoded, "deadbeef");
let decoded = hex_decode(&encoded).unwrap();
assert_eq!(decoded, vec![0xde, 0xad, 0xbe, 0xef]);
}
#[test]
fn hex_decode_none_on_failure() {
test_host::reset();
assert!(hex_decode("zz").is_none());
}
#[test]
fn url_encode_passes_input() {
test_host::reset();
test_host::with_mock(|m| {
m.encoding_url_encode_response = Some("hello%20world".into());
});
assert_eq!(url_encode("hello world"), "hello%20world");
assert_eq!(
test_host::read_mock(|m| m.last_encoding_url_encode_input.clone()),
Some("hello world".into())
);
}
#[test]
fn url_decode_returns_some() {
test_host::reset();
test_host::with_mock(|m| {
m.encoding_url_decode_response = Some("hello world".into());
});
assert_eq!(url_decode("hello%20world").as_deref(), Some("hello world"));
}
#[test]
fn url_decode_none_on_failure() {
test_host::reset();
assert!(url_decode("%ZZ").is_none());
}
}