diff --git a/quickfix/src/lib.rs b/quickfix/src/lib.rs index a3d293b..b3a62b7 100644 --- a/quickfix/src/lib.rs +++ b/quickfix/src/lib.rs @@ -178,9 +178,7 @@ pub trait SessionContainer { /// Borrow mutable session to the container. /// /// Session is lookup using its ID. - fn with_session_mut(&self, session_id: SessionId, f: F) -> Result - where - F: FnOnce(&mut Session) -> T; + fn session(&self, session_id: SessionId) -> Result, QuickFixError>; } /// Convert object to FIX value. diff --git a/quickfix/src/session.rs b/quickfix/src/session.rs index c8b756d..822b637 100644 --- a/quickfix/src/session.rs +++ b/quickfix/src/session.rs @@ -1,4 +1,4 @@ -use std::fmt; +use std::{fmt, marker::PhantomData}; use quickfix_ffi::{ FixSession_isLoggedOn, FixSession_logout, FixSession_lookup, FixSession_send, @@ -17,9 +17,12 @@ pub fn send_to_target(msg: Message, session_id: &SessionId) -> Result<(), QuickF } /// FIX Session. -pub struct Session(pub(crate) FixSession_t); +pub struct Session<'a> { + pub(crate) inner: FixSession_t, + pub(crate) phantom_container: PhantomData<&'a ()>, +} -impl Session { +impl Session<'static> { /// Find a session by its ID. /// /// # Safety @@ -27,32 +30,37 @@ impl Session { /// Function is unsafe because there is no way to bind FIX session lifetime /// to rust session lifetime. /// - /// Use `SessionContainer::with_session_mut` instead. It will give you a safe scope + /// Use `SessionContainer::session` instead. It will give you a safe scope /// where session has been borrowed to the acceptor / initiator. pub unsafe fn lookup(session_id: &SessionId) -> Result { match unsafe { FixSession_lookup(session_id.0) } { - Some(session) => Ok(Self(session)), + Some(inner) => Ok(Self { + inner, + phantom_container: PhantomData, + }), None => Err(QuickFixError::from_last_error()), } } +} +impl Session<'_> { /// Force session logout. pub fn logout(&mut self) -> Result<(), QuickFixError> { - ffi_code_to_result(unsafe { FixSession_logout(self.0) }) + ffi_code_to_result(unsafe { FixSession_logout(self.inner) }) } /// Check if session is logged on. pub fn is_logged_on(&mut self) -> Result { - ffi_code_to_bool(unsafe { FixSession_isLoggedOn(self.0) }) + ffi_code_to_bool(unsafe { FixSession_isLoggedOn(self.inner) }) } /// Send message using current session. pub fn send(&mut self, msg: Message) -> Result { - ffi_code_to_bool(unsafe { FixSession_send(self.0, msg.0) }) + ffi_code_to_bool(unsafe { FixSession_send(self.inner, msg.0) }) } } -impl fmt::Debug for Session { +impl fmt::Debug for Session<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("Session").finish() } diff --git a/quickfix/src/socket_acceptor.rs b/quickfix/src/socket_acceptor.rs index 0001e2a..e36767b 100644 --- a/quickfix/src/socket_acceptor.rs +++ b/quickfix/src/socket_acceptor.rs @@ -9,7 +9,7 @@ use quickfix_ffi::{ use crate::{ utils::{ffi_code_to_bool, ffi_code_to_result}, Application, ApplicationCallback, ConnectionHandler, FfiMessageStoreFactory, LogCallback, - LogFactory, QuickFixError, Session, SessionContainer, SessionSettings, + LogFactory, QuickFixError, Session, SessionContainer, SessionId, SessionSettings, }; /// Socket implementation of incoming connections handler. @@ -95,19 +95,17 @@ where S: FfiMessageStoreFactory, L: LogCallback, { - fn with_session_mut(&self, session_id: crate::SessionId, f: F) -> Result - where - F: FnOnce(&mut Session) -> T, - { - let mut session = unsafe { + fn session(&self, session_id: SessionId) -> Result, QuickFixError> { + unsafe { FixSocketAcceptor_getSession(self.inner, session_id.0) - .map(Session) + .map(|inner| Session { + inner, + phantom_container: PhantomData, + }) .ok_or_else(|| { QuickFixError::SessionNotFound(format!("No session found: {session_id:?}")) - })? - }; - - Ok(f(&mut session)) + }) + } } } diff --git a/quickfix/src/socket_initiator.rs b/quickfix/src/socket_initiator.rs index 76b88af..55d8d32 100644 --- a/quickfix/src/socket_initiator.rs +++ b/quickfix/src/socket_initiator.rs @@ -10,7 +10,7 @@ use quickfix_ffi::{ use crate::{ utils::{ffi_code_to_bool, ffi_code_to_result}, Application, ApplicationCallback, ConnectionHandler, FfiMessageStoreFactory, LogCallback, - LogFactory, QuickFixError, Session, SessionContainer, SessionSettings, + LogFactory, QuickFixError, Session, SessionContainer, SessionId, SessionSettings, }; /// Socket implementation of establishing connections handler. @@ -96,19 +96,17 @@ where S: FfiMessageStoreFactory, L: LogCallback, { - fn with_session_mut(&self, session_id: crate::SessionId, f: F) -> Result - where - F: FnOnce(&mut Session) -> T, - { - let mut session = unsafe { + fn session(&self, session_id: SessionId) -> Result, QuickFixError> { + unsafe { FixSocketInitiator_getSession(self.inner, session_id.0) - .map(Session) + .map(|inner| Session { + inner, + phantom_container: PhantomData, + }) .ok_or_else(|| { QuickFixError::SessionNotFound(format!("No session found: {session_id:?}")) - })? - }; - - Ok(f(&mut session)) + }) + } } } diff --git a/quickfix/tests/test_send_receive.rs b/quickfix/tests/test_send_receive.rs index 28b9036..9edcd47 100644 --- a/quickfix/tests/test_send_receive.rs +++ b/quickfix/tests/test_send_receive.rs @@ -99,44 +99,42 @@ fn test_full_fix_application() -> Result<(), QuickFixError> { // Send a message from one app to the other using `SessionContainer` API. // - Check first session are not mixable from sender / receiver. assert_eq!( - socket_sender.with_session_mut(ServerType::Receiver.session_id(), |_session| { - unreachable!(); - }), - Err(QuickFixError::SessionNotFound( + socket_sender + .session(ServerType::Receiver.session_id()) + .unwrap_err(), + QuickFixError::SessionNotFound( "No session found: SessionId(\"FIX.4.4:ME->THEIR\")".to_string() - )) + ) ); assert_eq!( - socket_receiver.with_session_mut(ServerType::Sender.session_id(), |_session| { - unreachable!(); - }), - Err(QuickFixError::SessionNotFound( + socket_receiver + .session(ServerType::Sender.session_id()) + .unwrap_err(), + QuickFixError::SessionNotFound( "No session found: SessionId(\"FIX.4.4:THEIR->ME\")".to_string() - )) + ) ); // Then play with API 😎 - socket_sender.with_session_mut(ServerType::Sender.session_id(), |session| { - let news = build_news("Hello", &[])?; - session.send(news)?; + let news = build_news("Hello", &[])?; + socket_sender + .session(ServerType::Sender.session_id())? + .send(news)?; - Ok::<_, QuickFixError>(()) - })??; thread::sleep(Duration::from_millis(50)); assert_eq!(sender.user_msg_count(), MsgCounter { sent: 2, recv: 1 }); assert_eq!(receiver.user_msg_count(), MsgCounter { sent: 1, recv: 2 }); - socket_receiver.with_session_mut(ServerType::Receiver.session_id(), |session| { - let news = build_news( - "Anyone here", - &["This news have", "some content", "that is very interesting"], - )?; - session.send(news)?; + let news = build_news( + "Anyone here", + &["This news have", "some content", "that is very interesting"], + )?; + socket_receiver + .session(ServerType::Receiver.session_id())? + .send(news)?; - Ok::<_, QuickFixError>(()) - })??; thread::sleep(Duration::from_millis(50)); assert_eq!(sender.user_msg_count(), MsgCounter { sent: 2, recv: 2 });