nix_bindings_util/
string_return.rs

1use anyhow::Result;
2
3/// Callback for nix_store_get_uri and other functions that return a string.
4///
5/// This function is used by the other nix_* crates, and you should never need to call it yourself.
6///
7/// Some functions in the nix library "return" strings without giving you ownership over them, by letting you pass a callback function that gets to look at that string. This callback simply turns that string pointer into an owned rust String.
8///
9/// # Safety
10///
11/// _Manual memory management_
12///
13/// Only for passing to the nix C API. Do not call this function directly.
14pub unsafe extern "C" fn callback_get_result_string(
15    start: *const ::std::os::raw::c_char,
16    n: std::os::raw::c_uint,
17    user_data: *mut std::os::raw::c_void,
18) {
19    let ret = user_data as *mut Result<String>;
20
21    if start.is_null() {
22        if n != 0 {
23            panic!("callback_get_result_string: start is null but n is not zero");
24        }
25        *ret = Ok(String::new());
26        return;
27    }
28
29    let slice = std::slice::from_raw_parts(start as *const u8, n as usize);
30
31    if (*ret).is_ok() {
32        panic!(
33            "callback_get_result_string: Result must be initialized to Err. Did Nix call us twice?"
34        );
35    }
36
37    *ret = String::from_utf8(slice.to_vec())
38        .map_err(|e| anyhow::format_err!("Nix string is not valid UTF-8: {}", e));
39}
40
41pub fn callback_get_result_string_data(vec: &mut Result<String>) -> *mut std::os::raw::c_void {
42    vec as *mut Result<String> as *mut std::os::raw::c_void
43}
44
45#[macro_export]
46macro_rules! result_string_init {
47    () => {
48        Err(anyhow::anyhow!("String was not set by Nix C API"))
49    };
50}
51
52#[cfg(test)]
53mod tests {
54    use super::*;
55    use nix_bindings_util_sys as raw;
56
57    /// Typecheck the function signature against the generated bindings in nix_bindings_util_sys.
58    static _CALLBACK_GET_RESULT_STRING: raw::get_string_callback = Some(callback_get_result_string);
59
60    #[test]
61    fn test_callback_get_result_string_empty() {
62        let mut ret: Result<String> = result_string_init!();
63        let start: *const std::os::raw::c_char = std::ptr::null();
64        let n: std::os::raw::c_uint = 0;
65        let user_data: *mut std::os::raw::c_void = callback_get_result_string_data(&mut ret);
66
67        unsafe {
68            callback_get_result_string(start, n, user_data);
69        }
70
71        let s = ret.unwrap();
72        assert_eq!(s, "");
73    }
74
75    #[test]
76    fn test_callback_result_string() {
77        let mut ret: Result<String> = result_string_init!();
78        let start = b"helloGARBAGE".as_ptr() as *const std::os::raw::c_char;
79        let n: std::os::raw::c_uint = 5;
80        let user_data: *mut std::os::raw::c_void = callback_get_result_string_data(&mut ret);
81        unsafe {
82            callback_get_result_string(start, n, user_data);
83        }
84
85        let s = ret.unwrap();
86        assert_eq!(s, "hello");
87    }
88}