use std::{ffi::c_char, str::FromStr};
use nautilus_core::{
MUTEX_POISONED,
ffi::{
abort_on_panic,
string::{cstr_as_str, str_to_cstr},
},
};
use crate::{currencies::CURRENCY_MAP, enums::CurrencyType, types::Currency};
#[unsafe(no_mangle)]
pub unsafe extern "C" fn currency_from_py(
code_ptr: *const c_char,
precision: u8,
iso4217: u16,
name_ptr: *const c_char,
currency_type: CurrencyType,
) -> Currency {
Currency::new(
unsafe { cstr_as_str(code_ptr) },
precision,
iso4217,
unsafe { cstr_as_str(name_ptr) },
currency_type,
)
}
#[unsafe(no_mangle)]
pub extern "C" fn currency_to_cstr(currency: &Currency) -> *const c_char {
str_to_cstr(format!("{currency:?}").as_str())
}
#[unsafe(no_mangle)]
pub extern "C" fn currency_code_to_cstr(currency: &Currency) -> *const c_char {
str_to_cstr(¤cy.code)
}
#[unsafe(no_mangle)]
pub extern "C" fn currency_name_to_cstr(currency: &Currency) -> *const c_char {
str_to_cstr(¤cy.name)
}
#[unsafe(no_mangle)]
pub extern "C" fn currency_hash(currency: &Currency) -> u64 {
currency.code.precomputed_hash()
}
#[unsafe(no_mangle)]
pub extern "C" fn currency_register(currency: Currency) {
abort_on_panic(|| {
CURRENCY_MAP
.lock()
.unwrap()
.insert(currency.code.to_string(), currency);
});
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn currency_exists(code_ptr: *const c_char) -> u8 {
abort_on_panic(|| {
let code = unsafe { cstr_as_str(code_ptr) };
u8::from(
CURRENCY_MAP
.lock()
.expect(MUTEX_POISONED)
.contains_key(code),
)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn currency_from_cstr(code_ptr: *const c_char) -> Currency {
abort_on_panic(|| {
let code = unsafe { cstr_as_str(code_ptr) };
Currency::from_str(code).unwrap()
})
}
#[cfg(test)]
mod tests {
use std::ffi::{CStr, CString};
use rstest::rstest;
use super::*;
use crate::{enums::CurrencyType, types::Currency};
#[rstest]
fn test_registration() {
let currency = Currency::new("MYC", 4, 0, "My Currency", CurrencyType::Crypto);
currency_register(currency);
unsafe {
assert_eq!(currency_exists(str_to_cstr("MYC")), 1);
}
}
#[rstest]
fn test_currency_from_py() {
let code = CString::new("MYC").unwrap();
let name = CString::new("My Currency").unwrap();
let currency = unsafe {
super::currency_from_py(code.as_ptr(), 4, 0, name.as_ptr(), CurrencyType::Crypto)
};
assert_eq!(currency.code.as_str(), "MYC");
assert_eq!(currency.name.as_str(), "My Currency");
assert_eq!(currency.currency_type, CurrencyType::Crypto);
}
#[rstest]
fn test_currency_to_cstr() {
let currency = Currency::USD();
let cstr = unsafe { CStr::from_ptr(currency_to_cstr(¤cy)) };
let expected_output = format!("{currency:?}");
assert_eq!(cstr.to_str().unwrap(), expected_output);
}
#[rstest]
fn test_currency_code_to_cstr() {
let currency = Currency::USD();
let cstr = unsafe { CStr::from_ptr(currency_code_to_cstr(¤cy)) };
assert_eq!(cstr.to_str().unwrap(), "USD");
}
#[rstest]
fn test_currency_name_to_cstr() {
let currency = Currency::USD();
let cstr = unsafe { CStr::from_ptr(currency_name_to_cstr(¤cy)) };
assert_eq!(cstr.to_str().unwrap(), "United States dollar");
}
#[rstest]
fn test_currency_hash() {
let currency = Currency::USD();
let hash = super::currency_hash(¤cy);
assert_eq!(hash, currency.code.precomputed_hash());
}
#[rstest]
fn test_currency_from_cstr() {
let code = CString::new("USD").unwrap();
let currency = unsafe { currency_from_cstr(code.as_ptr()) };
assert_eq!(currency, Currency::USD());
}
}