divvunspell 1.0.0-beta.3

Spell checking library for ZHFST/BHFST spellers, with case handling and tokenization support.
Documentation
use libc::c_char;
use std::ffi::{CStr, CString};

use crate::tokenizer::{Tokenize, WordIndices};

pub(crate) mod fbs;

#[no_mangle]
pub extern "C" fn divvun_word_indices<'a>(utf8_string: *const c_char) -> *mut WordIndices<'a> {
    let c_str = unsafe { CStr::from_ptr(utf8_string) };
    let string = c_str.to_str().unwrap();
    let iterator = string.word_indices();
    Box::into_raw(Box::new(iterator)) as *mut _
}

#[no_mangle]
pub extern "C" fn divvun_word_indices_next<'a>(
    iterator: *mut WordIndices<'a>,
    out_index: *mut u64,
    out_string: *mut *const c_char,
) -> u8 {
    let iterator = unsafe { &mut *iterator };

    match iterator.next() {
        Some((index, word)) => {
            let c_word = CString::new(word).unwrap();
            unsafe {
                *out_index = index as u64;
                *out_string = c_word.into_raw();
            }
            1
        }
        None => 0,
    }
}

#[no_mangle]
pub extern "C" fn divvun_word_indices_free<'a>(handle: *mut WordIndices<'a>) {
    drop(unsafe { Box::from_raw(handle) });
}

#[no_mangle]
pub extern "C" fn divvun_cstr_free(handle: *mut c_char) {
    drop(unsafe { CString::from_raw(handle) });
}

use crate::ffi::fbs::IntoFlatbuffer;
use crate::tokenizer::WordContext;
use cffi::{FromForeign, Slice, ToForeign};
use std::convert::Infallible;

pub struct FbsMarshaler;

impl cffi::ReturnType for FbsMarshaler {
    type Foreign = Slice<u8>;
    type ForeignTraitObject = ();

    fn foreign_default() -> Self::Foreign {
        Slice::default()
    }
}

impl<T: IntoFlatbuffer> ToForeign<T, Slice<u8>> for FbsMarshaler {
    type Error = Infallible;

    fn to_foreign(bufferable: T) -> Result<Slice<u8>, Self::Error> {
        let vec = bufferable.into_flatbuffer();
        cffi::VecMarshaler::to_foreign(vec)
    }
}

#[no_mangle]
pub unsafe extern "C" fn divvun_fbs_free(slice: Slice<u8>) {
    cffi::VecMarshaler::from_foreign(slice)
        .map(|_| ())
        .unwrap_or(());
}

#[cfg(feature = "logging")]
#[no_mangle]
pub unsafe extern "C" fn divvun_enable_logging() {
    env_logger::init();
}

#[doc(hidden)]
/// This makes the cffi go brrr.
pub unsafe extern "C" fn _cffi_string_free(ptr: Slice<u8>) {
    cffi::ffi::cffi_string_free(ptr);
}

#[cffi::marshal(return_marshaler = "FbsMarshaler")]
pub extern "C" fn divvun_cursor_context(
    #[marshal(cffi::StrMarshaler)] first_half: &str,
    #[marshal(cffi::StrMarshaler)] second_half: &str,
) -> WordContext {
    crate::tokenizer::cursor_context(first_half, second_half)
}

#[cfg(all(test, feature = "internal_ffi"))]
mod tests {
    use crate::ffi::fbs::IntoFlatbuffer;

    #[test]
    fn fbs() {
        let word_context = crate::tokenizer::cursor_context("this is some", " text");
        println!("{:?}", &word_context);

        let buf = word_context.into_flatbuffer();
        println!("{:?}", &buf);

        let word_context = crate::ffi::fbs::tokenizer::get_root_as_word_context(&buf);
        println!(
            "{:?} {:?}",
            &word_context.current().index(),
            &word_context.current().value()
        );
    }
}