byond/
call.rs

1//! Helpers for managing `call()()`s from BYOND code.
2use std::cell::RefCell;
3use std::ffi::{CStr, CString, NulError};
4use std::os::raw::{c_char, c_int};
5use std::ptr::null;
6use std::slice;
7
8thread_local! {
9    static BYOND_RETURN: RefCell<Option<CString>> = {
10        RefCell::new(None)
11    };
12}
13
14/// Returns a pointer that can be returned to BYOND from a `call()()`ed function,
15/// to return a string.
16pub fn return_to_byond<T: AsRef<[u8]>>(string: T) -> Result<*const c_char, NulError> {
17    let cstr = CString::new(string.as_ref())?;
18    let ptr = cstr.as_ptr();
19
20    BYOND_RETURN.with(|f| {
21        *f.borrow_mut() = Some(cstr);
22    });
23
24    Ok(ptr)
25}
26
27/// Turns the arguments supplied by BYOND into a more workable vector.
28///
29/// All strings are converted into UTF-8 losilly. You've been warned.
30pub unsafe fn from_byond_args(n: c_int, v: *const *const c_char) -> Vec<String> {
31    let mut args = Vec::new();
32    let slice = slice::from_raw_parts(v, n as usize);
33    for ptr in slice {
34        let cstr = CStr::from_ptr(*ptr);
35        let string = String::from_utf8_lossy(cstr.to_bytes()).into_owned();
36        args.push(string);
37    }
38    args
39}
40
41/// Allows one to easily test BYOND callbacks.
42/// Does *not* take arguments to pass down.
43///
44/// Warning: This passes a straight nullptr to the function's argument list.
45/// As such, if the function expects any arguments, this will probably segfault.
46/// Non-UTF-8 returned strings are lossily converted.
47/// # Panics
48/// Panics if the strings in args contain a NUL byte.
49pub fn test_byond_call(
50    func: unsafe extern "C" fn(i32, *const *const c_char) -> *const c_char,
51) -> String {
52    unsafe {
53        let ptr = func(0, null());
54        CStr::from_ptr(ptr).to_string_lossy().into_owned()
55    }
56}
57
58/// Allows one to easily test BYOND callbacks.
59/// Takes arguments that are passed down to the function.
60///
61/// # Panics
62/// Panics if the strings in args contain a NUL byte.
63/// Non-UTF-8 strings are lossily converted.
64pub fn test_byond_call_args<P>(
65    func: unsafe extern "C" fn(i32, *const *const c_char) -> *const c_char,
66    args: &[P],
67) -> String
68where
69    P: AsRef<[u8]>,
70{
71    // Need to keep track of the CStrs so they dont Drop.
72    let mut cstrs = Vec::with_capacity(args.len());
73    let mut ptrs = Vec::with_capacity(args.len());
74
75    for arg in args {
76        let arg = arg.as_ref();
77        let cstr = CString::new(arg).unwrap();
78        let ptr = cstr.as_ptr();
79        cstrs.push(cstr);
80        ptrs.push(ptr);
81    }
82
83    unsafe {
84        let ptr = func(ptrs.len() as i32, ptrs.as_slice().as_ptr());
85        CStr::from_ptr(ptr).to_string_lossy().into_owned()
86    }
87}