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 pub(crate) ctx: ffi::solClient_opaqueContext_pt,
21}
22
23static SOLACE_GLOBAL_INIT: OnceLock<i32> = OnceLock::new();
24
25impl RawContext {
26 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 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#[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}