use std::ffi::CStr;
use std::str;
use std::fs::File;
use std::io::Cursor;
use std::ffi::CString;
use std::error::Error;
use std::panic::catch_unwind;
use std::panic::RefUnwindSafe;
use libc::c_char;
use core::qm;
use core::dedup::DedupControl;
use facade::handle::extern_handle as eh;
use facade::handle::Handle;
use facade;
#[repr(C)]
pub enum QmDedupControl {
QmErrorIfMissing,
QmInline,
QmWriteOnce
}
#[no_mangle]
pub extern "C" fn qm_instrument_from_json_file(
source: *const c_char,
dedup_ccy: QmDedupControl,
number_of_currencies: u32,
currencies: *const u64,
dedup_instr: QmDedupControl,
number_of_instruments: u32,
instruments: *const u64) -> u64 {
return_handle(&|| {
let mut file = open_c_file(source)?;
let mut ccy_vec = Vec::with_capacity(number_of_currencies as usize);
for i in 0..(number_of_currencies as isize) {
unsafe { ccy_vec.push(eh::as_currency(*currencies.offset(i))?) };
}
let mut instr_vec = Vec::with_capacity(number_of_instruments as usize);
for i in 0..(number_of_instruments as isize) {
unsafe { instr_vec.push(eh::as_instrument(*instruments.offset(i))?) };
}
let result = facade::instrument_from_json(&mut file,
convert_dedup(&dedup_ccy), &ccy_vec,
convert_dedup(&dedup_instr), &instr_vec);
Ok(Handle::from_instrument(result))
})
}
#[no_mangle]
pub extern "C" fn qm_instrument_from_json_string(
source: *const c_char,
dedup_ccy: QmDedupControl,
number_of_currencies: u32,
currencies: *const u64,
dedup_instr: QmDedupControl,
number_of_instruments: u32,
instruments: *const u64) -> u64 {
return_handle(&|| {
let bytes = bytes_from_c_string(source);
let mut ccy_vec = Vec::with_capacity(number_of_currencies as usize);
for i in 0..(number_of_currencies as isize) {
unsafe { ccy_vec.push(eh::as_currency(*currencies.offset(i))?) };
}
let mut instr_vec = Vec::with_capacity(number_of_instruments as usize);
for i in 0..(number_of_instruments as isize) {
unsafe { instr_vec.push(eh::as_instrument(*instruments.offset(i))?) };
}
let result = facade::instrument_from_json(&mut Cursor::new(bytes),
convert_dedup(&dedup_ccy), &ccy_vec,
convert_dedup(&dedup_instr), &instr_vec);
Ok(Handle::from_instrument(result))
})
}
#[no_mangle]
pub extern "C" fn qm_currency_from_json_file(source: *const c_char) -> u64 {
return_handle(&|| {
let mut file = open_c_file(source)?;
let result = facade::currency_from_json(&mut file);
Ok(Handle::from_currency(result))
})
}
#[no_mangle]
pub extern "C" fn qm_currency_from_json_string(source: *const c_char) -> u64 {
return_handle(&|| {
let bytes = bytes_from_c_string(source);
let result = facade::currency_from_json(&mut Cursor::new(bytes));
Ok(Handle::from_currency(result))
})
}
#[no_mangle]
pub extern "C" fn qm_pricer_factory_from_json_file(source: *const c_char) -> u64 {
return_handle(&|| {
let mut file = open_c_file(source)?;
let result = facade::pricer_factory_from_json(&mut file);
Ok(Handle::from_pricer_factory(result))
})
}
#[no_mangle]
pub extern "C" fn qm_pricer_factory_from_json_string(source: *const c_char) -> u64 {
return_handle(&|| {
let bytes = bytes_from_c_string(source);
let result = facade::pricer_factory_from_json(&mut Cursor::new(bytes));
Ok(Handle::from_pricer_factory(result))
})
}
#[no_mangle]
pub extern "C" fn qm_fixing_table_from_json_file(source: *const c_char) -> u64 {
return_handle(&|| {
let mut file = open_c_file(source)?;
let result = facade::fixing_table_from_json(&mut file);
Ok(Handle::from_fixing_table(result))
})
}
#[no_mangle]
pub extern "C" fn qm_fixing_table_from_json_string(source: *const c_char) -> u64 {
return_handle(&|| {
let bytes = bytes_from_c_string(source);
let result = facade::fixing_table_from_json(&mut Cursor::new(bytes));
Ok(Handle::from_fixing_table(result))
})
}
#[no_mangle]
pub extern "C" fn qm_market_data_from_json_file(source: *const c_char) -> u64 {
return_handle(&|| {
let mut file = open_c_file(source)?;
let result = facade::market_data_from_json(&mut file);
Ok(Handle::from_market_data(result))
})
}
#[no_mangle]
pub extern "C" fn qm_market_data_from_json_string(source: *const c_char) -> u64 {
return_handle(&|| {
let bytes = bytes_from_c_string(source);
let result = facade::market_data_from_json(&mut Cursor::new(bytes));
Ok(Handle::from_market_data(result))
})
}
#[no_mangle]
pub extern "C" fn qm_report_generator_from_json_file(source: *const c_char) -> u64 {
return_handle(&|| {
let mut file = open_c_file(source)?;
let result = facade::report_generator_from_json(&mut file);
Ok(Handle::from_report_generator(result))
})
}
#[no_mangle]
pub extern "C" fn qm_report_generator_from_json_string(source: *const c_char) -> u64 {
return_handle(&|| {
let bytes = bytes_from_c_string(source);
let result = facade::report_generator_from_json(&mut Cursor::new(bytes));
Ok(Handle::from_report_generator(result))
})
}
#[no_mangle]
pub extern "C" fn qm_reports_from_json_file(source: *const c_char) -> u64 {
return_handle(&|| {
let mut file = open_c_file(source)?;
let result = facade::reports_from_json(&mut file);
Ok(Handle::from_reports(result))
})
}
#[no_mangle]
pub extern "C" fn qm_reports_from_json_string(source: *const c_char) -> u64 {
return_handle(&|| {
let bytes = bytes_from_c_string(source);
let result = facade::reports_from_json(&mut Cursor::new(bytes));
Ok(Handle::from_reports(result))
})
}
#[no_mangle]
pub extern "C" fn qm_reports_as_json_string(handle: u64) -> *mut c_char {
let reports_string = match catch_unwind(||
CString::new(eh::reports_as_string(handle)).unwrap()) {
Ok(result) => result,
Err(_) => CString::new("Caught panic when converting result string").unwrap()
};
reports_string.into_raw()
}
#[no_mangle]
pub extern "C" fn qm_calculate(pricer_factory: u64, instrument: u64,
fixing_table: u64, market_data: u64,
number_of_report_generators: u32, report_generators: *const u64) -> u64 {
return_handle(&|| {
let pf = eh::as_pricer_factory(pricer_factory)?;
let instr = eh::as_instrument(instrument)?;
let fix = eh::as_fixing_table(fixing_table)?;
let mkt = eh::as_market_data(market_data)?;
let mut gen = Vec::with_capacity(number_of_report_generators as usize);
for i in 0..(number_of_report_generators as isize) {
unsafe { gen.push(eh::as_report_generator(*report_generators.offset(i))?) };
}
let reports = facade::calculate(pf, instr, fix, mkt, &gen);
Ok(Handle::from_reports(reports))
})
}
#[no_mangle]
pub extern "C" fn qm_assert_approx_eq_reports(reports_freed: u64, expected_freed: u64,
tol_price: f64, tol_ccy_risk: f64, tol_unit_risk: f64) -> u64 {
return_handle(&|| {
let reports = eh::as_reports(reports_freed);
let expected = eh::as_reports(expected_freed);
let result = facade::assert_approx_eq_reports(&reports?, &expected?,
tol_price, tol_ccy_risk, tol_unit_risk);
Ok(Handle::from_empty(result))
})
}
#[no_mangle]
pub extern "C" fn qm_is_error(handle: u64) -> bool {
eh::is_error(handle)
}
#[no_mangle]
pub extern "C" fn qm_error_string(handle: u64) -> *mut c_char {
let error_string = match catch_unwind(||
CString::new(eh::as_error(handle).description()).unwrap()) {
Ok(result) => result,
Err(_) => CString::new("Caught panic when converting error string").unwrap()
};
error_string.into_raw()
}
#[no_mangle]
pub extern "C" fn qm_free_string(string: *mut c_char) {
let _ = unsafe { CString::from_raw(string) };
}
#[no_mangle]
pub extern "C" fn qm_clone(handle: u64) -> u64 {
match catch_unwind(|| eh::clone_handle(handle)) {
Ok(handle) => handle,
Err(_) => eh::from_handle(Ok(Handle::from_error(qm::Error::new("Caught panic during clone"))))
}
}
#[no_mangle]
pub extern "C" fn qm_free_handle(handle: u64) {
if let Err(_) = catch_unwind(|| eh::free_handle(handle)) {
eprint!("Caught panic during qm_free_handle");
}
}
fn open_c_file(source: *const c_char) -> Result<File, qm::Error> {
let source_bytes = unsafe { CStr::from_ptr(source).to_bytes() };
let filename = str::from_utf8(source_bytes)?;
let file = File::open(filename)?;
Ok(file)
}
fn bytes_from_c_string<'a>(source: *const c_char) -> &'a[u8] {
unsafe { CStr::from_ptr(source).to_bytes() }
}
fn return_handle<F>(f: &F) -> u64
where
F: Fn() -> Result<Handle, qm::Error>,
F: RefUnwindSafe
{
let result = match catch_unwind(|| f() ) {
Ok(result) => result,
Err(_) => Err(qm::Error::new("Caught panic when creating handle"))
};
eh::from_handle(result)
}
fn convert_dedup(dedup: &QmDedupControl) -> DedupControl {
match dedup {
&QmDedupControl::QmErrorIfMissing => DedupControl::ErrorIfMissing,
&QmDedupControl::QmInline => DedupControl::Inline,
&QmDedupControl::QmWriteOnce => DedupControl::WriteOnce
}
}
#[cfg(test)]
pub mod tests {
use super::*;
use facade::tests::{sample_currency_json, sample_forward_european_json, sample_fixing_table_json,
sample_market_data_json, sample_pricer_factory_json, sample_report_generator_json,
sample_results_json, sample_equity_json};
use std::ffi::CString;
use std::ptr::null;
#[test]
fn c_interface_load_currency() {
let ccy_c = CString::new(sample_currency_json()).unwrap();
let currency = qm_currency_from_json_string(ccy_c.as_ptr());
if qm_is_error(currency) {
let message = qm_error_string(currency);
let message_bytes = unsafe { CStr::from_ptr(message).to_bytes() };
let message_str = String::from_utf8(message_bytes.to_vec()).unwrap();
print!("Error: {}", message_str);
qm_free_string(message);
assert!(false);
}
qm_free_handle(currency);
}
#[test]
fn c_interface_load_equity() {
let ccy_c = CString::new(sample_currency_json()).unwrap();
let currency = qm_currency_from_json_string(ccy_c.as_ptr());
if qm_is_error(currency) {
let message = qm_error_string(currency);
let message_bytes = unsafe { CStr::from_ptr(message).to_bytes() };
let message_str = String::from_utf8(message_bytes.to_vec()).unwrap();
print!("Error: {}", message_str);
qm_free_string(message);
assert!(false);
}
let currencies = vec![currency];
let instruments = vec![0_u64; 0];
let equity_c = CString::new(sample_equity_json()).unwrap();
let equity = qm_instrument_from_json_string(equity_c.as_ptr(),
QmDedupControl::QmWriteOnce, 1_u32, currencies.as_ptr(),
QmDedupControl::QmWriteOnce, 0_u32, instruments.as_ptr());
if qm_is_error(equity) {
let message = qm_error_string(equity);
let message_bytes = unsafe { CStr::from_ptr(message).to_bytes() };
let message_str = String::from_utf8(message_bytes.to_vec()).unwrap();
print!("Error: {}", message_str);
qm_free_string(message);
assert!(false);
}
qm_free_handle(currency);
qm_free_handle(equity);
}
#[test]
fn c_interface_forward_starting_european_price() {
price_european_using_c_interface().unwrap();
}
fn price_european_using_c_interface() -> Result<(), qm::Error> {
let pf_c = CString::new(sample_pricer_factory_json()).unwrap();
let pricer_factory = qm_pricer_factory_from_json_string(pf_c.as_ptr());
if qm_is_error(pricer_factory) {
return convert_error(pricer_factory);
}
let instr_c = CString::new(sample_forward_european_json()).unwrap();
let european = qm_instrument_from_json_string(instr_c.as_ptr(),
QmDedupControl::QmInline, 0, null(), QmDedupControl::QmInline, 0, null());
if qm_is_error(european) {
return convert_error(european);
}
let mkt_c = CString::new(sample_market_data_json()).unwrap();
let market_data = qm_market_data_from_json_string(mkt_c.as_ptr());
if qm_is_error(market_data) {
return convert_error(market_data);
}
let fix_c = CString::new(sample_fixing_table_json()).unwrap();
let fixing_table = qm_fixing_table_from_json_string(fix_c.as_ptr());
if qm_is_error(market_data) {
return convert_error(market_data);
}
let dg_c = CString::new(sample_report_generator_json()).unwrap();
let delta_gamma = qm_report_generator_from_json_string(dg_c.as_ptr());
if qm_is_error(delta_gamma) {
return convert_error(delta_gamma);
}
let report_generators = vec![delta_gamma];
let reports = qm_calculate(pricer_factory, european, fixing_table, market_data,
report_generators.len() as u32, report_generators.as_ptr());
if qm_is_error(reports) {
return convert_error(reports);
}
qm_free_handle(pricer_factory);
qm_free_handle(european);
qm_free_handle(fixing_table);
qm_free_handle(market_data);
qm_free_handle(delta_gamma);
let expected_c = CString::new(sample_results_json()).unwrap();
let expected = qm_reports_from_json_string(expected_c.as_ptr());
if qm_is_error(expected) {
return convert_error(expected);
}
let errors = qm_assert_approx_eq_reports(reports, expected, 1e-12, 1e-12, 1e-12);
if qm_is_error(errors) {
return convert_error(errors);
}
qm_free_handle(errors);
Ok(())
}
fn convert_error(handle: u64) -> Result<(), qm::Error> {
let message = qm_error_string(handle);
let message_bytes = unsafe { CStr::from_ptr(message).to_bytes() };
let message_str = String::from_utf8(message_bytes.to_vec()).unwrap();
qm_free_string(message);
Err(qm::Error::new(&message_str))
}
}