solace_rs/
context.rs

1use crate::session::builder::SessionBuilder;
2use crate::session::builder::SessionBuilderError;
3use crate::util::get_last_error_info;
4use crate::Session;
5use crate::{ContextError, SolClientReturnCode, SolaceLogLevel};
6use solace_rs_sys as ffi;
7use std::mem;
8use std::ptr;
9use std::sync::Mutex;
10use std::sync::OnceLock;
11use tracing::warn;
12
13use crate::message::InboundMessage;
14use crate::session::SessionEvent;
15use std::sync::Arc;
16type Result<T> = std::result::Result<T, ContextError>;
17
18pub(super) struct RawContext {
19    // This pointer must never be allowed to leave the struct
20    pub(crate) ctx: ffi::solClient_opaqueContext_pt,
21}
22
23static SOLACE_GLOBAL_INIT: OnceLock<i32> = OnceLock::new();
24
25impl RawContext {
26    /// .
27    /// Raw solace context that wraps around the c context
28    ///
29    /// # Errors
30    ///
31    /// This function will return an error if .
32    ///
33    /// # Safety
34    /// Context initializes global variables so it is not safe to have multiple solace contexts.
35    /// .
36    pub unsafe fn new(log_level: SolaceLogLevel) -> Result<Self> {
37        let rc = SOLACE_GLOBAL_INIT
38            .get_or_init(|| ffi::solClient_initialize(log_level as u32, ptr::null_mut()));
39
40        let rc = SolClientReturnCode::from_raw(*rc);
41
42        if !rc.is_ok() {
43            let subcode = get_last_error_info();
44            return Err(ContextError::InitializationFailed(rc, subcode));
45        }
46        let mut ctx: ffi::solClient_opaqueContext_pt = ptr::null_mut();
47        let mut context_func: ffi::solClient_context_createFuncInfo_t =
48            ffi::solClient_context_createFuncInfo {
49                regFdInfo: ffi::solClient_context_createRegisterFdFuncInfo {
50                    regFdFunc_p: None,
51                    unregFdFunc_p: None,
52                    user_p: ptr::null_mut(),
53                },
54            };
55
56        // enable context thread
57        let mut context_props: [*const i8; 3] = [
58            solace_rs_sys::SOLCLIENT_CONTEXT_PROP_CREATE_THREAD.as_ptr() as *const i8,
59            solace_rs_sys::SOLCLIENT_PROP_ENABLE_VAL.as_ptr() as *const i8,
60            ptr::null(),
61        ];
62
63        let solace_context_raw_rc = unsafe {
64            ffi::solClient_context_create(
65                context_props.as_mut_ptr(),
66                &mut ctx,
67                &mut context_func,
68                mem::size_of::<ffi::solClient_context_createRegisterFdFuncInfo>(),
69            )
70        };
71
72        let rc = SolClientReturnCode::from_raw(solace_context_raw_rc);
73
74        if !rc.is_ok() {
75            let subcode = get_last_error_info();
76            return Err(ContextError::InitializationFailed(rc, subcode));
77        }
78        Ok(Self { ctx })
79    }
80}
81
82impl Drop for RawContext {
83    fn drop(&mut self) {
84        let return_code = unsafe { ffi::solClient_context_destroy(&mut self.ctx) };
85        if return_code != ffi::solClient_returnCode_SOLCLIENT_OK {
86            warn!("Solace context did not drop properly");
87        }
88    }
89}
90
91unsafe impl Send for RawContext {}
92
93/// Handle for a Solace context, used to create sessions.
94///
95/// It is thread safe, and can be safely cloned and shared. Each clone
96/// references the same underlying C context. Internally, an `Arc` is
97/// used to implement this in a threadsafe way.
98///
99/// Also note that this binding deviates from the C API in that each
100/// session created from a context initially owns a clone of that
101/// context.
102///
103///
104#[derive(Clone)]
105pub struct Context {
106    pub(super) raw: Arc<Mutex<RawContext>>,
107}
108
109impl Context {
110    pub fn new(log_level: SolaceLogLevel) -> std::result::Result<Self, ContextError> {
111        Ok(Self {
112            raw: Arc::new(Mutex::new(unsafe { RawContext::new(log_level) }?)),
113        })
114    }
115
116    pub fn session_builder<Host, Vpn, Username, Password, OnMessage, OnEvent>(
117        &self,
118    ) -> SessionBuilder<Host, Vpn, Username, Password, OnMessage, OnEvent> {
119        SessionBuilder::new(self.clone())
120    }
121
122    pub fn session<'session, Host, Vpn, Username, Password, OnMessage, OnEvent>(
123        &self,
124        host_name: Host,
125        vpn_name: Vpn,
126        username: Username,
127        password: Password,
128        on_message: Option<OnMessage>,
129        on_event: Option<OnEvent>,
130    ) -> std::result::Result<Session<'session, OnMessage, OnEvent>, SessionBuilderError>
131    where
132        Host: Into<Vec<u8>>,
133        Vpn: Into<Vec<u8>>,
134        Username: Into<Vec<u8>>,
135        Password: Into<Vec<u8>>,
136        OnMessage: FnMut(InboundMessage) + Send + 'session,
137        OnEvent: FnMut(SessionEvent) + Send + 'session,
138    {
139        let mut builder = SessionBuilder::new(self.clone())
140            .host_name(host_name)
141            .vpn_name(vpn_name)
142            .username(username)
143            .password(password);
144
145        if let Some(on_message) = on_message {
146            builder = builder.on_message(on_message);
147        }
148
149        if let Some(on_event) = on_event {
150            builder = builder.on_event(on_event);
151        }
152
153        builder.build()
154    }
155}