diff --git a/quickfix-bind/include/quickfix_bind.h b/quickfix-bind/include/quickfix_bind.h index c2eb475..a2212a9 100644 --- a/quickfix-bind/include/quickfix_bind.h +++ b/quickfix-bind/include/quickfix_bind.h @@ -54,11 +54,14 @@ extern "C" int FixSocketAcceptor_isStopped(const FixSocketAcceptor_t *obj); void FixSocketAcceptor_delete(FixSocketAcceptor_t *obj); + FixSessionID_t *FixSessionID_new(const char *beginString, const char *senderCompID, const char *targetCompID, const char *sessionQualifier); const char *FixSessionID_getBeginString(const FixSessionID_t *session); const char *FixSessionID_getSenderCompID(const FixSessionID_t *session); const char *FixSessionID_getTargetCompID(const FixSessionID_t *session); const char *FixSessionID_getSessionQualifier(const FixSessionID_t *session); int8_t FixSessionID_isFIXT(const FixSessionID_t *session); + const char *FixSessionID_toString(const FixSessionID *session); + void FixSessionID_delete(FixSessionID_t *session); FixMessage_t *FixMessage_new(); int FixMessage_setField(const FixMessage_t *obj, int tag, const char *value); diff --git a/quickfix-bind/src/quickfix_bind.cpp b/quickfix-bind/src/quickfix_bind.cpp index d2f957e..18bf78a 100644 --- a/quickfix-bind/src/quickfix_bind.cpp +++ b/quickfix-bind/src/quickfix_bind.cpp @@ -327,6 +327,24 @@ extern "C" DELETE_OBJ(FIX::SocketAcceptor, obj); } + FixSessionID_t * + FixSessionID_new(const char *beginString, const char *senderCompID, const char *targetCompID, const char *sessionQualifier) + { + RETURN_VAL_IF_NULL(beginString, NULL); + RETURN_VAL_IF_NULL(senderCompID, NULL); + RETURN_VAL_IF_NULL(targetCompID, NULL); + RETURN_VAL_IF_NULL(sessionQualifier, NULL); + + try + { + return (FixSessionID_t *)(new FIX::SessionID(beginString, senderCompID, targetCompID, sessionQualifier)); + } + catch (std::exception &ex) + { + return NULL; + } + } + const char *FixSessionID_getBeginString(const FixSessionID_t *session) { RETURN_VAL_IF_NULL(session, NULL); @@ -353,19 +371,40 @@ extern "C" int8_t FixSessionID_isFIXT(const FixSessionID_t *session) { - RETURN_VAL_IF_NULL(session, 0); + RETURN_VAL_IF_NULL(session, ERRNO_INVAL); auto fix_obj = (FIX::SessionID *)(session); try { - return fix_obj->isFIXT(); + return fix_obj->isFIXT() ? 1 : 0; } catch (std::exception &e) { - return 0; + return ERRNO_EXCEPTION; } } + const char *FixSessionID_toString(const FixSessionID *session) + { + RETURN_VAL_IF_NULL(session, NULL); + + auto fix_obj = (FIX::SessionID *)(session); + try + { + return fix_obj->toStringFrozen().c_str(); + } + catch (std::exception &e) + { + return NULL; + } + } + + void FixSessionID_delete(FixSessionID_t *session) + { + RETURN_IF_NULL(session); + DELETE_OBJ(FIX::SessionID, session); + } + FixMessage_t * FixMessage_new() { diff --git a/quickfix-ffi/src/lib.rs b/quickfix-ffi/src/lib.rs index 60d0872..623e898 100644 --- a/quickfix-ffi/src/lib.rs +++ b/quickfix-ffi/src/lib.rs @@ -84,11 +84,19 @@ extern "C" { pub fn FixSocketAcceptor_isStopped(obj: FixSocketAcceptor_t) -> ffi::c_int; pub fn FixSocketAcceptor_delete(obj: FixSocketAcceptor_t); + pub fn FixSessionID_new( + beginString: *const ffi::c_char, + senderCompID: *const ffi::c_char, + targetCompID: *const ffi::c_char, + sessionQualifier: *const ffi::c_char, + ) -> Option; pub fn FixSessionID_getBeginString(obj: FixSessionID_t) -> Option>; pub fn FixSessionID_getSenderCompID(obj: FixSessionID_t) -> Option>; pub fn FixSessionID_getTargetCompID(obj: FixSessionID_t) -> Option>; pub fn FixSessionID_getSessionQualifier(obj: FixSessionID_t) -> Option>; pub fn FixSessionID_isFIXT(obj: FixSessionID_t) -> i8; + pub fn FixSessionID_toString(obj: FixSessionID_t) -> Option>; + pub fn FixSessionID_delete(obj: FixSessionID_t); pub fn FixMessage_new() -> Option; #[must_use] diff --git a/quickfix/src/session_id.rs b/quickfix/src/session_id.rs index 64b6230..e94ac6c 100644 --- a/quickfix/src/session_id.rs +++ b/quickfix/src/session_id.rs @@ -1,14 +1,40 @@ +use std::{ffi::CString, fmt}; + use quickfix_ffi::{ - FixSessionID_getBeginString, FixSessionID_getSenderCompID, FixSessionID_getSessionQualifier, - FixSessionID_getTargetCompID, FixSessionID_isFIXT, + FixSessionID_delete, FixSessionID_getBeginString, FixSessionID_getSenderCompID, + FixSessionID_getSessionQualifier, FixSessionID_getTargetCompID, FixSessionID_isFIXT, + FixSessionID_toString, }; -use crate::utils::read_checked_cstr; +use crate::{utils::read_checked_cstr, QuickFixError}; -#[derive(Debug)] pub struct SessionId(pub(crate) quickfix_ffi::FixSessionID_t); impl SessionId { + pub fn try_new( + begin_string: &str, + sender_comp_id: &str, + target_comp_id: &str, + session_qualifier: &str, + ) -> Result { + let ffi_begin_string = CString::new(begin_string)?; + let ffi_sender_comp_id = CString::new(sender_comp_id)?; + let ffi_target_comp_id = CString::new(target_comp_id)?; + let ffi_session_qualifier = CString::new(session_qualifier)?; + + match unsafe { + quickfix_ffi::FixSessionID_new( + ffi_begin_string.as_ptr(), + ffi_sender_comp_id.as_ptr(), + ffi_target_comp_id.as_ptr(), + ffi_session_qualifier.as_ptr(), + ) + } { + Some(val) => Ok(Self(val)), + None => Err(QuickFixError::InvalidFunctionReturn), + } + } + pub fn get_begin_string(&self) -> Option { unsafe { FixSessionID_getBeginString(self.0) }.map(read_checked_cstr) } @@ -29,4 +55,22 @@ impl SessionId { let val = unsafe { FixSessionID_isFIXT(self.0) }; val != 0 } + + pub fn as_string(&self) -> String { + unsafe { FixSessionID_toString(self.0) } + .map(read_checked_cstr) + .unwrap_or_default() + } +} + +impl fmt::Debug for SessionId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("SessionId").field(&self.as_string()).finish() + } +} + +impl Drop for SessionId { + fn drop(&mut self) { + unsafe { FixSessionID_delete(self.0) } + } } diff --git a/quickfix/tests/test_session_id.rs b/quickfix/tests/test_session_id.rs new file mode 100644 index 0000000..851f90d --- /dev/null +++ b/quickfix/tests/test_session_id.rs @@ -0,0 +1,20 @@ +use quickfix::SessionId; + +#[test] +fn test_new() { + let session = SessionId::try_new("FIX.4.1", "FOO", "BAR", "").unwrap(); + assert_eq!(session.get_begin_string().as_deref(), Some("FIX.4.1")); + assert_eq!(session.get_sender_comp_id().as_deref(), Some("FOO")); + assert_eq!(session.get_target_comp_id().as_deref(), Some("BAR")); + assert_eq!(session.get_session_qualifier().as_deref(), Some("")); + assert_eq!(session.as_string(), "FIX.4.1:FOO->BAR"); +} + +#[test] +fn test_fixt() { + let session1 = SessionId::try_new("FIX.4.1", "FOO", "BAR", "").unwrap(); + let session2 = SessionId::try_new("FIXT", "FOO", "BAR", "").unwrap(); + + assert!(!session1.is_fixt()); + assert!(session2.is_fixt()); +}