diff --git a/quickfix-bind/include/quickfix_bind.h b/quickfix-bind/include/quickfix_bind.h index a2212a9..136581c 100644 --- a/quickfix-bind/include/quickfix_bind.h +++ b/quickfix-bind/include/quickfix_bind.h @@ -55,12 +55,13 @@ extern "C" void FixSocketAcceptor_delete(FixSocketAcceptor_t *obj); FixSessionID_t *FixSessionID_new(const char *beginString, const char *senderCompID, const char *targetCompID, const char *sessionQualifier); + FixSessionID_t *FixSessionID_copy(const FixSessionID_t *src); 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); + const char *FixSessionID_toString(const FixSessionID_t *session); void FixSessionID_delete(FixSessionID_t *session); FixMessage_t *FixMessage_new(); diff --git a/quickfix-bind/src/quickfix_bind.cpp b/quickfix-bind/src/quickfix_bind.cpp index 18bf78a..85e9924 100644 --- a/quickfix-bind/src/quickfix_bind.cpp +++ b/quickfix-bind/src/quickfix_bind.cpp @@ -345,6 +345,22 @@ extern "C" } } + FixSessionID_t * + FixSessionID_copy(const FixSessionID_t *src) + { + RETURN_VAL_IF_NULL(src, NULL); + + auto fix_obj = (FIX::SessionID *)(src); + try + { + return (FixSessionID_t *)(new FIX::SessionID(*fix_obj)); + } + catch (std::exception &ex) + { + return NULL; + } + } + const char *FixSessionID_getBeginString(const FixSessionID_t *session) { RETURN_VAL_IF_NULL(session, NULL); diff --git a/quickfix-ffi/src/lib.rs b/quickfix-ffi/src/lib.rs index 623e898..2a2fa57 100644 --- a/quickfix-ffi/src/lib.rs +++ b/quickfix-ffi/src/lib.rs @@ -19,7 +19,7 @@ pub struct FixFileLogFactory_t(NonNull); #[repr(transparent)] pub struct FixApplication_t(NonNull); -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(transparent)] pub struct FixSessionID_t(NonNull); @@ -90,6 +90,7 @@ extern "C" { targetCompID: *const ffi::c_char, sessionQualifier: *const ffi::c_char, ) -> Option; + pub fn FixSessionID_copy(src: FixSessionID_t) -> 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>; diff --git a/quickfix/src/session_id.rs b/quickfix/src/session_id.rs index e94ac6c..2f3cc3c 100644 --- a/quickfix/src/session_id.rs +++ b/quickfix/src/session_id.rs @@ -1,14 +1,14 @@ use std::{ffi::CString, fmt}; use quickfix_ffi::{ - FixSessionID_delete, FixSessionID_getBeginString, FixSessionID_getSenderCompID, - FixSessionID_getSessionQualifier, FixSessionID_getTargetCompID, FixSessionID_isFIXT, - FixSessionID_toString, + FixSessionID_copy, FixSessionID_delete, FixSessionID_getBeginString, + FixSessionID_getSenderCompID, FixSessionID_getSessionQualifier, FixSessionID_getTargetCompID, + FixSessionID_isFIXT, FixSessionID_new, FixSessionID_t, FixSessionID_toString, }; use crate::{utils::read_checked_cstr, QuickFixError}; -pub struct SessionId(pub(crate) quickfix_ffi::FixSessionID_t); +pub struct SessionId(pub(crate) FixSessionID_t); impl SessionId { pub fn try_new( @@ -23,7 +23,7 @@ impl SessionId { let ffi_session_qualifier = CString::new(session_qualifier)?; match unsafe { - quickfix_ffi::FixSessionID_new( + FixSessionID_new( ffi_begin_string.as_ptr(), ffi_sender_comp_id.as_ptr(), ffi_target_comp_id.as_ptr(), @@ -35,6 +35,10 @@ impl SessionId { } } + pub fn try_clone(&self) -> Option { + unsafe { FixSessionID_copy(self.0) }.map(Self) + } + pub fn get_begin_string(&self) -> Option { unsafe { FixSessionID_getBeginString(self.0) }.map(read_checked_cstr) } @@ -63,6 +67,12 @@ impl SessionId { } } +impl Clone for SessionId { + fn clone(&self) -> Self { + self.try_clone().expect("Fail to copy SessionID") + } +} + impl fmt::Debug for SessionId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("SessionId").field(&self.as_string()).finish() @@ -74,3 +84,17 @@ impl Drop for SessionId { unsafe { FixSessionID_delete(self.0) } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_clone_have_distinct_ptr() { + let session1 = SessionId::try_new("FIX.4.1", "FOO", "BAR", "").unwrap(); + + let session2 = session1.clone(); + assert_ne!(session1.0, session2.0); + assert_eq!(session1.as_string(), session2.as_string()); + } +} diff --git a/quickfix/tests/test_session_id.rs b/quickfix/tests/test_session_id.rs index 851f90d..a300460 100644 --- a/quickfix/tests/test_session_id.rs +++ b/quickfix/tests/test_session_id.rs @@ -18,3 +18,16 @@ fn test_fixt() { assert!(!session1.is_fixt()); assert!(session2.is_fixt()); } + +#[test] +fn test_clone() { + let session1 = SessionId::try_new("FIX.4.1", "FOO", "BAR", "").unwrap(); + assert_eq!(session1.as_string(), "FIX.4.1:FOO->BAR"); + + let session2 = session1.clone(); + assert_eq!(session2.as_string(), "FIX.4.1:FOO->BAR"); + + // Test do not crash after drop + drop(session1); + assert_eq!(session2.as_string(), "FIX.4.1:FOO->BAR"); +}