utf8proc 0.1.2

Rust bindings to the utf8proc library
Documentation
//! Niche functionality, redundant with main features.
//!
//! Using this for anything except testing is most likely a bug.

use super::{TransformOptions, advanced::convert_callback};

/// Implements the [`map_advanced`](crate::transform::advanced::map_advanced) method
/// with a call to the underlying utf8proc library.
///
/// This means the result is allocated with [`malloc`](libc::malloc),
/// not the standard rust allocator.
/// Since this is niche functionality, there is no "simple" wrapper.
///
/// It is a bug if this returns a different result from the advanced map function.
#[allow(clippy::cast_possible_wrap)]
pub fn ffi_map_advanced(
    str: &str,
    options: TransformOptions,
    mut transform: Option<&mut dyn FnMut(char) -> char>,
) -> Result<mbox::MArray<u8>, crate::Error> {
    // SAFETY: Result not assumed to be UTF8
    let raw_options = unsafe { options.to_ffi() };
    let mut dest_buffer: *mut u8 = std::ptr::null_mut();
    // SAFETY: Caller only gives valid codepoints, and doesn't mess with callback data
    let (callback, callback_data) = unsafe { convert_callback(&mut transform) };
    // SAFETY: We have passed a correctly allocated string
    let res_code = unsafe {
        utf8proc_sys::utf8proc_map_custom(
            str.as_ptr(),
            str.len() as isize,
            &raw mut dest_buffer,
            raw_options,
            callback,
            callback_data,
        )
    };
    if res_code < 0 {
        Err(crate::Error::from_code(res_code))
    } else {
        assert!(!dest_buffer.is_null());
        let len = res_code.cast_unsigned();
        // SAFETY: Guaranteed to be valid pointer, null-terminated, and valid UTF8
        let res = unsafe { mbox::MArray::from_raw(dest_buffer.cast()) };
        // TODO: MArray::from_raw recomputes strlen, even though it is already known
        assert_eq!(res.len(), len);
        Ok(res)
    }
}