1mod events;
2
3use std::{
4 ffi::{c_char, CStr, CString},
5 ptr::null_mut,
6 str::FromStr,
7};
8
9use twips::scramble::{
10 derive_scramble_for_event_seeded, random_scramble_for_event,
11 scramble_finder::free_memory_for_all_scramble_finders, DerivationSalt, DerivationSeed, Event,
12};
13
14fn unwrap_cstr_result_or_null_ptr(result: Result<*const c_char, ()>) -> *const c_char {
15 result.unwrap_or(null_mut())
16}
17
18fn war_cstr_to_rust_str_ref<'a>(cstr: *const c_char) -> Result<&'a str, ()> {
19 let cstr = unsafe { CStr::from_ptr(cstr) };
20 cstr.to_str().map_err(|_| ())
21}
22
23fn rust_str_to_raw_cstr(s: &str) -> *const c_char {
25 CString::new(s).unwrap().into_raw()
26}
27
28#[no_mangle]
36pub unsafe extern "C" fn ffi_random_scramble_for_event(
37 event_raw_cstr: *const c_char,
38) -> *const c_char {
39 unwrap_cstr_result_or_null_ptr(ffi_random_scramble_for_event_internal(event_raw_cstr))
40}
41
42fn ffi_random_scramble_for_event_internal(
43 event_raw_cstr: *const c_char,
44) -> Result<*const c_char, ()> {
45 let event_str = war_cstr_to_rust_str_ref(event_raw_cstr)?;
46 let event = Event::try_from(event_str).map_err(|_| ())?;
47 let result_str = random_scramble_for_event(event)
48 .map_err(|_| ())?
49 .to_string();
50 Ok(rust_str_to_raw_cstr(&result_str))
51}
52
53#[no_mangle]
61pub extern "C" fn ffi_derive_scramble_for_event(
62 hex_derivation_seed_cstr: *const c_char,
63 derivation_salt_hierarchy_str: *const c_char,
65 subevent_str: *const c_char,
66) -> *const c_char {
67 unwrap_cstr_result_or_null_ptr(ffi_derive_scramble_for_event_internal(
68 hex_derivation_seed_cstr,
69 derivation_salt_hierarchy_str,
70 subevent_str,
71 ))
72}
73
74fn ffi_derive_scramble_for_event_internal(
75 hex_derivation_seed_raw_cstr: *const c_char,
76 derivation_salt_hierarchy_raw_cstr: *const c_char,
78 subevent_raw_cstr: *const c_char,
79) -> Result<*const c_char, ()> {
80 let hex_derivation_seed_str = war_cstr_to_rust_str_ref(hex_derivation_seed_raw_cstr)?;
81 let derivation_salt_hierarchy_str =
82 war_cstr_to_rust_str_ref(derivation_salt_hierarchy_raw_cstr)?;
83 let subevent_str = war_cstr_to_rust_str_ref(subevent_raw_cstr)?;
84
85 let derivation_seed = DerivationSeed::from_str(hex_derivation_seed_str).map_err(|_| ())?;
86 let hierarchy = if derivation_salt_hierarchy_str.is_empty() {
87 vec![]
88 } else {
89 derivation_salt_hierarchy_str
90 .split("/")
91 .map(DerivationSalt::from_str)
92 .collect::<Result<Vec<DerivationSalt>, String>>()
93 .map_err(|_| ())?
94 };
95 let subevent = Event::try_from(subevent_str)
96 .map_err(|e| e.description)
97 .map_err(|_| ())?;
98 match derive_scramble_for_event_seeded(&derivation_seed, &hierarchy, subevent) {
99 Ok(scramble) => Ok(rust_str_to_raw_cstr(&scramble.to_string())),
100 Err(_) => Err(()),
101 }
102}
103
104#[no_mangle]
105pub extern "C" fn ffi_free_memory_for_all_scramble_finders() -> u32 {
106 free_memory_for_all_scramble_finders() as u32
109}
110
111#[test]
112fn ffi_test() {
113 let test_data = [
115 ("222", 11, 13), ("pyram", 11, 15), ("333", 15, 30),
118 ("555", 60, 60),
119 ("666", 80, 80),
120 ("777", 100, 100),
121 ("minx", 83, 83),
122 ];
123
124 let dylib_path = test_cdylib::build_current_project();
125 let lib = unsafe { libloading::Library::new(dylib_path).unwrap() };
126 let func: libloading::Symbol<unsafe extern "C" fn(event_raw_cstr: *mut c_char) -> *mut c_char> =
127 unsafe { lib.get(b"ffi_random_scramble_for_event").unwrap() };
128 for (event_id, min_num_moves, max_num_moves) in test_data {
129 let event_raw_cstr = CString::new((event_id).to_owned()).unwrap().into_raw();
130 let scramble_raw_cstr = unsafe { func(event_raw_cstr) };
131 let scramble_cstr = unsafe { CStr::from_ptr(scramble_raw_cstr) };
132 let scramble_str = scramble_cstr.to_str().map_err(|_| ()).unwrap();
133 let alg = scramble_str.parse::<cubing::alg::Alg>().unwrap();
134 assert!(alg.nodes.len() >= min_num_moves);
135 assert!(alg.nodes.len() <= max_num_moves);
136 }
137}