rune_modules/
base64.rs

1use base64::prelude::*;
2use rune::alloc::fmt::TryWrite;
3use rune::alloc::{String, Vec};
4use rune::runtime::Bytes;
5use rune::runtime::{Formatter, VmResult};
6use rune::{vm_panic, ContextError, Module};
7
8#[rune::module(::base64)]
9/// Correct and fast [base64] encoding based on the [`base64`] crate.
10///
11/// [base64]: https://developer.mozilla.org/en-US/docs/Glossary/Base64
12/// [`base64`]: https://docs.rs/base64
13///
14/// # Examples
15///
16/// ```rune
17/// let encoded = base64::encode(b"\xFF\xEC\x20\x55\0");
18/// assert_eq!(base64::decode(encoded), Ok(b"\xFF\xEC\x20\x55\0"));
19/// ```
20pub fn module(_stdio: bool) -> Result<Module, ContextError> {
21    let mut module = Module::from_meta(self::module_meta)?;
22
23    module.ty::<DecodeError>()?;
24
25    module.function_meta(decode)?;
26    module.function_meta(encode)?;
27    Ok(module)
28}
29
30/// Decode a base64 String into data
31///
32/// # Examples
33///
34/// ```rune
35/// assert_eq!(base64::decode("+uwgVQA=")?, b"\xFA\xEC\x20\x55\0");
36/// ```
37#[rune::function(vm_result)]
38fn decode(inp: &str) -> Result<Bytes, DecodeError> {
39    // estimate the max size
40    let decoded_size = base64::decoded_len_estimate(inp.len());
41
42    // try to allocate enough bytes
43    let mut v = Vec::new();
44    v.try_resize(decoded_size, 0).vm?;
45
46    // decode
47    let len = BASE64_STANDARD.decode_slice(inp, &mut v)?;
48    v.truncate(len);
49    Ok(Bytes::from_vec(v))
50}
51
52/// Encode a data into a base64 String.
53///
54/// # Examples
55///
56/// ```rune
57/// assert_eq!(base64::encode(b"\xFF\xEC\x20\x55\0"), "/+wgVQA=");
58/// ```
59#[rune::function(vm_result)]
60fn encode(bytes: &[u8]) -> String {
61    let Some(encoded_size) = base64::encoded_len(bytes.len(), true) else {
62        vm_panic!("encoded input length overflows usize");
63    };
64
65    let mut buf = Vec::new();
66    buf.try_resize(encoded_size, 0).vm?;
67
68    // this should never panic
69    if let Err(e) = BASE64_STANDARD.encode_slice(bytes, &mut buf) {
70        vm_panic!(e);
71    }
72
73    // base64 should only return valid utf8 strings
74    let string = match String::from_utf8(buf) {
75        Ok(s) => s,
76        Err(e) => vm_panic!(e),
77    };
78
79    string
80}
81
82/// Errors that can occur while decoding.
83#[derive(Debug, rune::Any)]
84#[rune(item = ::base64)]
85#[allow(dead_code)]
86pub struct DecodeError {
87    inner: base64::DecodeSliceError,
88}
89
90impl From<base64::DecodeSliceError> for DecodeError {
91    fn from(inner: base64::DecodeSliceError) -> Self {
92        Self { inner }
93    }
94}
95
96impl DecodeError {
97    #[rune::function(instance, protocol = DISPLAY_FMT)]
98    fn display_fmt(&self, f: &mut Formatter) -> VmResult<()> {
99        rune::vm_write!(f, "{}", self.inner)
100    }
101}