-1

I use snips and built a C library. I want to connect the library to my Node environment with Rust.

JavaScript

var ffi = require('ffi');
const ArrayType = require('ref-array');
var nlu = require('./nlu');

const StringArray = ArrayType('string');


var nlulib = '../cargo/target/x86_64-apple-darwin/release/libp_public_transport_nlu.dylib'

var nlu = ffi.Library(nlulib, {
    "load": ['pointer', ['string']],
    "execute": ['string', ['pointer', 'string', StringArray]]
});


var ptrToEngine = nlu.load("../snips_public_transport_engine");
var responseNLU = nlu.execute(ptrToEngine, "myQuery", ['bestIntent'], ['worstIntent']);

Rust

#[no_mangle]
pub extern "C" fn execute(engine_pointer: *mut SnipsNluEngine, query: *const c_char, whitelist: &[u8], blacklist: &[u8]) -> CString {    
    let query_c_str = unsafe { CStr::from_ptr(query) };
    let query_string = match query_c_str.to_str().map(|s| s.to_owned()){
        Ok(string) => string,
        Err(e) => e.to_string()
    };

    let engine = unsafe {
        assert!(!engine_pointer.is_null());
        &mut *engine_pointer
    };

    let result = engine.parse(query_string.trim(), None, None).unwrap();
    let result_json = serde_json::to_string_pretty(&result).unwrap();

    CString::new(result_json).unwrap()
}

The engine.parse function expects Into<Option<Vec<&'a str>>> as a parameter instead of None, so I need to convert the whitelist and blacklist into this format.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
chocolate cake
  • 2,129
  • 5
  • 26
  • 48
  • It's hard to answer your question because it doesn't include a [MRE]. We can't tell what crates (and their versions), types, traits, fields, etc. are present in the code. It would make it easier for us to help you if you try to reproduce your error on the [Rust Playground](https://play.rust-lang.org) if possible, otherwise in a brand new Cargo project, then [edit] your question to include the additional info. There are [Rust-specific MRE tips](//stackoverflow.com/tags/rust/info) you can use to reduce your original code for posting here. Thanks! – Shepmaster Jan 15 '20 at 14:05
  • Notably, it seems like your problem has nothing to do with JavaScript or C, or maybe even FFI. It seems like you should be able to drastically reduce the code while making an example such that anyone can trivially see the error. – Shepmaster Jan 15 '20 at 14:07

1 Answers1

0

After much struggle with it, I found a solution. I know, this won't be the best that has ever been existing, but it's a solution :)

pub extern "C" fn execute(engine_pointer: *mut SnipsNluEngine, query: *const c_char, whitelist: *const *const c_char) -> CString {
    let query_c_str = unsafe { CStr::from_ptr(query) };
    let query_string = match query_c_str.to_str().map(|s| s.to_owned()){
        Ok(string) => string,
        Err(e) => e.to_string()
    };

    let engine = unsafe {
        assert!(!engine_pointer.is_null());
        &mut *engine_pointer
    };


    // count length of whitelist
    let mut whitelist_count = 0;
    let mut wc = whitelist;
    unsafe {
        while *wc != std::ptr::null() {
            whitelist_count += 1;
            wc = wc.offset(1);
        }
    }

    // get single elements pointer from pointer
    let sliced_whitelist = unsafe { slice::from_raw_parts(whitelist, whitelist_count) };

    // get string for every pointer
    let mut string_list_whitelist: Vec<String> = vec![];

    for i in 0..whitelist_count {
        let whitelist_element = sliced_whitelist[i];
        let whitelist_value = unsafe { CStr::from_ptr(whitelist_element) };

        let string_whitelist: String = match whitelist_value.to_str().map(|s| s.to_owned()){
            Ok(string) => string,
            Err(e) => e.to_string()
        };

        string_list_whitelist.insert(0, string_whitelist);
    }

    let mut snips_whitelist: Vec<&str> = vec![];

    for i in 0..whitelist_count {
        let whitelist_element_str: &str = &string_list_whitelist[i][..];

        snips_whitelist.insert(0, whitelist_element_str);
    }


    // create an optional for the whitelist
    let mut snips_whitelist_optional: Option<Vec<&str>> = None;

    if whitelist_count != 0 {
        snips_whitelist_optional = Some(snips_whitelist);
    }

    // parsing
    let result = engine.parse(query_string.trim(), snips_whitelist_optional, snips_blacklist_optional).unwrap();
    let result_json = serde_json::to_string_pretty(&result).unwrap();

    CString::new(result_json).unwrap()

}

Hint: a Nullpointer (e.g. ref.NULL) has to be sent at the end of the whitelist string array. Alternatively, you can send the array length as parameter instead of counting the list length.

chocolate cake
  • 2,129
  • 5
  • 26
  • 48
  • Did you look at the FFI already provided in the snips-nlu-rs repository ? It's here: https://github.com/snipsco/snips-nlu-rs/blob/master/ffi/src/lib.rs#L48-L65 – Adrien Ball Mar 24 '20 at 09:17