impl/rust/base64_url_decode.rs
1,684 bytes · the Rust implementation · view raw
Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
use super::funejson::Value; ← the fune runtime: the JSON value the test vectors use; fune build keeps it only where a signature takes one
use super::encoding_base64_base64_decode::base64_decode; ← base64Decode, another function of this group · built into the same file, even by a slim install
/// base64url text back to bytes. Padding is optional, since JWTs leave it
/// off and some producers keep it, but padding that is present must be right.
///
/// # Panics
/// Panics on a character outside the URL-safe alphabet, an impossible
/// length, wrong padding, or non-zero bits after the last byte.
pub fn base64_url_decode(text: &str) -> Vec<i64> {
let mut body = String::with_capacity(text.len() + 2);
let mut padding = 0usize;
for &b in text.as_bytes() {
if b == b'=' {
padding += 1;
continue;
}
let ok = b.is_ascii_alphanumeric() || b == b'-' || b == b'_';
if !ok || padding > 0 {
panic!("base64url text may only contain A-Z, a-z, 0-9, - and _, with optional = padding at the end");
}
body.push(match b {
b'-' => '+',
b'_' => '/',
other => other as char,
});
}
if body.len() % 4 == 1 {
panic!(
"base64url text cannot be {} characters long; no number of bytes encodes to that",
body.len()
);
}
let needed = (4 - body.len() % 4) % 4;
if padding != 0 && padding != needed {
panic!("base64url text has the wrong amount of = padding");
}
for _ in 0..needed {
body.push('=');
}
base64_decode(&body)
}
pub fn fune_vector(args: &[Value]) -> Value {
let text = match &args[0] {
Value::Str(s) => s.as_str(),
_ => panic!("base64url text must be a string"),
};
Value::Arr(base64_url_decode(text).into_iter().map(Value::Int).collect())
}