mod events;
use std::{
ffi::{c_char, CStr, CString},
ptr::null_mut,
str::FromStr,
};
use twips::scramble::{
derive_scramble_for_event_seeded, random_scramble_for_event,
scramble_finder::free_memory_for_all_scramble_finders, DerivationSalt, DerivationSeed, Event,
};
fn unwrap_cstr_result_or_null_ptr(result: Result<*const c_char, ()>) -> *const c_char {
result.unwrap_or(null_mut())
}
fn war_cstr_to_rust_str_ref<'a>(cstr: *const c_char) -> Result<&'a str, ()> {
let cstr = unsafe { CStr::from_ptr(cstr) };
cstr.to_str().map_err(|_| ())
}
fn rust_str_to_raw_cstr(s: &str) -> *const c_char {
CString::new(s).unwrap().into_raw()
}
#[no_mangle]
pub unsafe extern "C" fn ffi_random_scramble_for_event(
event_raw_cstr: *const c_char,
) -> *const c_char {
unwrap_cstr_result_or_null_ptr(ffi_random_scramble_for_event_internal(event_raw_cstr))
}
fn ffi_random_scramble_for_event_internal(
event_raw_cstr: *const c_char,
) -> Result<*const c_char, ()> {
let event_str = war_cstr_to_rust_str_ref(event_raw_cstr)?;
let event = Event::try_from(event_str).map_err(|_| ())?;
let result_str = random_scramble_for_event(event)
.map_err(|_| ())?
.to_string();
Ok(rust_str_to_raw_cstr(&result_str))
}
#[no_mangle]
pub extern "C" fn ffi_derive_scramble_for_event(
hex_derivation_seed_cstr: *const c_char,
derivation_salt_hierarchy_str: *const c_char,
subevent_str: *const c_char,
) -> *const c_char {
unwrap_cstr_result_or_null_ptr(ffi_derive_scramble_for_event_internal(
hex_derivation_seed_cstr,
derivation_salt_hierarchy_str,
subevent_str,
))
}
fn ffi_derive_scramble_for_event_internal(
hex_derivation_seed_raw_cstr: *const c_char,
derivation_salt_hierarchy_raw_cstr: *const c_char,
subevent_raw_cstr: *const c_char,
) -> Result<*const c_char, ()> {
let hex_derivation_seed_str = war_cstr_to_rust_str_ref(hex_derivation_seed_raw_cstr)?;
let derivation_salt_hierarchy_str =
war_cstr_to_rust_str_ref(derivation_salt_hierarchy_raw_cstr)?;
let subevent_str = war_cstr_to_rust_str_ref(subevent_raw_cstr)?;
let derivation_seed = DerivationSeed::from_str(hex_derivation_seed_str).map_err(|_| ())?;
let hierarchy = if derivation_salt_hierarchy_str.is_empty() {
vec![]
} else {
derivation_salt_hierarchy_str
.split("/")
.map(DerivationSalt::from_str)
.collect::<Result<Vec<DerivationSalt>, String>>()
.map_err(|_| ())?
};
let subevent = Event::try_from(subevent_str)
.map_err(|e| e.description)
.map_err(|_| ())?;
match derive_scramble_for_event_seeded(&derivation_seed, &hierarchy, subevent) {
Ok(scramble) => Ok(rust_str_to_raw_cstr(&scramble.to_string())),
Err(_) => Err(()),
}
}
#[no_mangle]
pub extern "C" fn ffi_free_memory_for_all_scramble_finders() -> u32 {
free_memory_for_all_scramble_finders() as u32
}
#[test]
fn ffi_test() {
let test_data = [
("222", 11, 13), ("pyram", 11, 15), ("333", 15, 30),
("555", 60, 60),
("666", 80, 80),
("777", 100, 100),
("minx", 83, 83),
];
let dylib_path = test_cdylib::build_current_project();
let lib = unsafe { libloading::Library::new(dylib_path).unwrap() };
let func: libloading::Symbol<unsafe extern "C" fn(event_raw_cstr: *mut c_char) -> *mut c_char> =
unsafe { lib.get(b"ffi_random_scramble_for_event").unwrap() };
for (event_id, min_num_moves, max_num_moves) in test_data {
let event_raw_cstr = CString::new((event_id).to_owned()).unwrap().into_raw();
let scramble_raw_cstr = unsafe { func(event_raw_cstr) };
let scramble_cstr = unsafe { CStr::from_ptr(scramble_raw_cstr) };
let scramble_str = scramble_cstr.to_str().map_err(|_| ()).unwrap();
let alg = scramble_str.parse::<cubing::alg::Alg>().unwrap();
assert!(alg.nodes.len() >= min_num_moves);
assert!(alg.nodes.len() <= max_num_moves);
}
}