use std::{ffi::CString, fmt, mem::ManuallyDrop};
use quickfix_ffi::{
FixMessage_addGroup, FixMessage_copy, FixMessage_copyGroup, FixMessage_copyHeader,
FixMessage_copyTrailer, FixMessage_delete, FixMessage_fromString, FixMessage_getField,
FixMessage_getGroupRef, FixMessage_getHeaderRef, FixMessage_getStringLen,
FixMessage_getTrailerRef, FixMessage_new, FixMessage_readString, FixMessage_removeField,
FixMessage_setField, FixMessage_t,
};
use crate::{
group::Group,
header::Header,
trailer::Trailer,
utils::{ffi_code_to_result, read_checked_cstr},
FieldMap, IntoFixValue, QuickFixError,
};
pub struct Message(pub(crate) FixMessage_t);
impl Message {
pub fn new() -> Self {
Self::default()
}
pub fn try_from_text(text: &str) -> Result<Self, QuickFixError> {
let ffi_text = CString::new(text)?;
unsafe { FixMessage_fromString(ffi_text.as_ptr()) }
.map(Self)
.ok_or_else(QuickFixError::from_last_error)
}
pub fn to_fix_raw_bytes(&self) -> Result<Vec<u8>, QuickFixError> {
unsafe {
let buffer_len = FixMessage_getStringLen(self.0)
.try_into()
.map_err(|_err| QuickFixError::from_last_error())?;
let mut buffer = vec![0_u8; buffer_len as usize];
assert_eq!(buffer.len(), buffer_len as usize);
ffi_code_to_result(FixMessage_readString(
self.0,
buffer.as_mut_ptr().cast(),
buffer_len,
))?;
Ok(buffer)
}
}
pub fn to_fix_string(&self) -> Result<String, QuickFixError> {
let buffer = self.to_fix_raw_bytes()?;
let text = CString::from_vec_with_nul(buffer).unwrap_or_default();
Ok(text.to_string_lossy().into())
}
pub fn clone_header(&self) -> Header {
unsafe { FixMessage_copyHeader(self.0) }
.map(Header)
.expect("Fail to allocate new Header")
}
pub fn with_header<T, F>(&self, f: F) -> T
where
F: FnOnce(&Header) -> T,
{
let ptr =
unsafe { FixMessage_getHeaderRef(self.0) }.expect("Fail to get ptr on message header");
let obj = ManuallyDrop::new(Header(ptr));
f(&obj)
}
pub fn with_header_mut<T, F>(&mut self, f: F) -> T
where
F: FnOnce(&mut Header) -> T,
{
let ptr =
unsafe { FixMessage_getHeaderRef(self.0) }.expect("Fail to get ptr on message header");
let mut obj = ManuallyDrop::new(Header(ptr));
f(&mut obj)
}
pub fn clone_trailer(&self) -> Trailer {
unsafe { FixMessage_copyTrailer(self.0) }
.map(Trailer)
.expect("Fail to allocate new Trailer")
}
pub fn with_trailer<T, F>(&self, f: F) -> T
where
F: FnOnce(&Trailer) -> T,
{
let ptr = unsafe { FixMessage_getTrailerRef(self.0) }
.expect("Fail to get ptr on message trailer");
let obj = ManuallyDrop::new(Trailer(ptr));
f(&obj)
}
pub fn with_trailer_mut<T, F>(&mut self, f: F) -> T
where
F: FnOnce(&mut Trailer) -> T,
{
let ptr = unsafe { FixMessage_getTrailerRef(self.0) }
.expect("Fail to get ptr on message trailer");
let mut obj = ManuallyDrop::new(Trailer(ptr));
f(&mut obj)
}
pub fn with_group<T, F>(&self, index: i32, tag: i32, f: F) -> Option<T>
where
F: FnOnce(&Group) -> T,
{
if let Some(ptr) = unsafe { FixMessage_getGroupRef(self.0, index, tag) } {
let obj = ManuallyDrop::new(Group(ptr));
Some(f(&obj))
} else {
None
}
}
pub fn with_group_mut<T, F>(&mut self, index: i32, tag: i32, f: F) -> Option<T>
where
F: FnOnce(&mut Group) -> T,
{
if let Some(ptr) = unsafe { FixMessage_getGroupRef(self.0, index, tag) } {
let mut obj = ManuallyDrop::new(Group(ptr));
Some(f(&mut obj))
} else {
None
}
}
}
impl FieldMap for Message {
fn get_field(&self, tag: i32) -> Option<String> {
unsafe { FixMessage_getField(self.0, tag) }.map(read_checked_cstr)
}
fn set_field<V: IntoFixValue>(&mut self, tag: i32, value: V) -> Result<(), QuickFixError> {
let fix_value = value.into_fix_value()?;
ffi_code_to_result(unsafe { FixMessage_setField(self.0, tag, fix_value.as_ptr()) })
}
fn remove_field(&mut self, tag: i32) -> Result<(), QuickFixError> {
ffi_code_to_result(unsafe { FixMessage_removeField(self.0, tag) })
}
fn add_group(&mut self, group: &Group) -> Result<(), QuickFixError> {
ffi_code_to_result(unsafe { FixMessage_addGroup(self.0, group.0) })?;
Ok(())
}
fn clone_group(&self, index: i32, tag: i32) -> Option<Group> {
unsafe { FixMessage_copyGroup(self.0, index, tag) }.map(Group)
}
}
impl Clone for Message {
fn clone(&self) -> Self {
Self(unsafe { FixMessage_copy(self.0) }.expect("Fail to clone Message"))
}
}
impl fmt::Debug for Message {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut printer = f.debug_tuple("Message");
if let Ok(txt) = self.to_fix_string() {
printer.field(&txt.replace(1 as char, "|"));
}
printer.finish()
}
}
impl Default for Message {
fn default() -> Self {
unsafe { FixMessage_new() }
.map(Self)
.expect("Fail to allocate new Message")
}
}
impl Drop for Message {
fn drop(&mut self) {
unsafe { FixMessage_delete(self.0) }
}
}