use std::ffi::CString;
use std::os::raw::c_void;
use crate::correlation::CorrelationId;
use crate::errors::{BlpError, Result};
use crate::event::Event;
use crate::options::SessionOptions;
use crate::request::Request;
use crate::service::Service;
struct HandlerShared {
f: Box<dyn Fn(Event) + Send + Sync + 'static>,
}
unsafe extern "C" fn event_trampoline(
event: *mut crate::ffi::blpapi_Event_t,
_session: *mut crate::ffi::blpapi_Session_t,
user_data: *mut c_void,
) {
if event.is_null() || user_data.is_null() {
return;
}
let shared = &*(user_data as *const HandlerShared);
let event = Event::from_raw(event);
(shared.f)(event);
}
pub struct AsyncSession {
ptr: *mut crate::ffi::blpapi_Session_t,
handler: Option<Box<HandlerShared>>,
}
unsafe impl Send for AsyncSession {}
unsafe impl Sync for AsyncSession {}
impl AsyncSession {
pub fn new(
options: &SessionOptions,
handler: impl Fn(Event) + Send + Sync + 'static,
) -> Result<Self> {
let shared = Box::new(HandlerShared {
f: Box::new(handler),
});
let user_data = &*shared as *const HandlerShared as *mut c_void;
let ptr = unsafe {
crate::ffi::blpapi_Session_create(
options.as_raw(),
Some(event_trampoline),
std::ptr::null_mut(),
user_data,
)
};
if ptr.is_null() {
return Err(BlpError::SessionStart {
source: None,
label: None,
});
}
Ok(Self {
ptr,
handler: Some(shared),
})
}
pub fn start(&self) -> Result<()> {
let rc = unsafe { crate::ffi::blpapi_Session_start(self.ptr) };
if rc != 0 {
return Err(BlpError::SessionStart {
source: None,
label: None,
});
}
Ok(())
}
pub fn stop(&self) {
unsafe {
crate::ffi::blpapi_Session_stop(self.ptr);
}
}
pub fn stop_async(&self) {
unsafe {
crate::ffi::blpapi_Session_stopAsync(self.ptr);
}
}
pub fn shutdown_nonblocking(self) {
self.stop_async();
std::mem::forget(self);
}
pub fn open_service(&self, name: &str) -> Result<()> {
let c_name = CString::new(name).map_err(|e| BlpError::InvalidArgument {
detail: format!("invalid service name: {}", e),
})?;
let rc = unsafe { crate::ffi::blpapi_Session_openService(self.ptr, c_name.as_ptr()) };
if rc != 0 {
return Err(BlpError::OpenService {
service: name.to_string(),
source: None,
label: None,
});
}
Ok(())
}
pub fn open_service_async(&self, name: &str, cid: &CorrelationId) -> Result<CorrelationId> {
let c_name = CString::new(name).map_err(|e| BlpError::InvalidArgument {
detail: format!("invalid service name: {}", e),
})?;
let mut cid_ffi = cid.to_ffi();
let rc = unsafe {
crate::ffi::blpapi_Session_openServiceAsync(self.ptr, c_name.as_ptr(), &mut cid_ffi)
};
if rc != 0 {
return Err(BlpError::OpenService {
service: name.to_string(),
source: None,
label: None,
});
}
Ok(CorrelationId::from_ffi(&cid_ffi))
}
pub fn get_service(&self, name: &str) -> Result<Service<'_>> {
let c_name = CString::new(name).map_err(|e| BlpError::InvalidArgument {
detail: format!("invalid service name: {}", e),
})?;
let mut service_ptr: *mut crate::ffi::blpapi_Service_t = std::ptr::null_mut();
let rc = unsafe {
crate::ffi::blpapi_Session_getService(self.ptr, &mut service_ptr, c_name.as_ptr())
};
if rc != 0 {
return Err(BlpError::OpenService {
service: name.to_string(),
source: None,
label: None,
});
}
Service::from_raw(service_ptr)
}
pub fn send_request(
&self,
req: &Request,
cid: Option<&CorrelationId>,
) -> Result<CorrelationId> {
self.send_request_with_label(req, cid, None)
}
pub fn send_request_with_label(
&self,
req: &Request,
cid: Option<&CorrelationId>,
label: Option<&str>,
) -> Result<CorrelationId> {
let mut cid_ffi = match cid {
Some(c) => c.to_ffi(),
None => CorrelationId::default().to_ffi(),
};
let (label_ptr, label_len, _label_cstring) = match label {
Some(value) => {
let cstring = CString::new(value).map_err(|e| BlpError::InvalidArgument {
detail: format!("invalid request label: {e}"),
})?;
(cstring.as_ptr(), value.len() as i32, Some(cstring))
}
None => (std::ptr::null(), 0, None),
};
let rc = unsafe {
crate::ffi::blpapi_Session_sendRequest(
self.ptr,
req.as_ptr(),
&mut cid_ffi,
std::ptr::null_mut(),
std::ptr::null_mut(),
label_ptr,
label_len,
)
};
if rc != 0 {
return Err(BlpError::Internal {
detail: format!("blpapi_Session_sendRequest failed with rc={}", rc),
});
}
Ok(CorrelationId::from_ffi(&cid_ffi))
}
pub fn cancel(&self, cid: &CorrelationId) -> Result<()> {
let cid_ffi = cid.to_ffi();
let rc = unsafe {
crate::ffi::blpapi_Session_cancel(self.ptr, &cid_ffi, 1, std::ptr::null(), 0)
};
if rc != 0 {
return Err(BlpError::Internal {
detail: format!("blpapi_Session_cancel failed with rc={}", rc),
});
}
Ok(())
}
pub fn subscribe(&self, subs: &crate::SubscriptionList, label: Option<&str>) -> Result<()> {
let (label_ptr, label_len, _label_cstring) = match label {
Some(l) => {
let cs = CString::new(l).map_err(|e| BlpError::InvalidArgument {
detail: format!("invalid subscription label: {}", e),
})?;
(cs.as_ptr(), l.len() as i32, Some(cs))
}
None => (std::ptr::null(), 0, None),
};
let rc = unsafe {
crate::ffi::blpapi_Session_subscribe(
self.ptr,
subs.as_ptr(),
std::ptr::null(),
label_ptr,
label_len,
)
};
if rc != 0 {
return Err(BlpError::Internal {
detail: format!("blpapi_Session_subscribe failed with rc={}", rc),
});
}
Ok(())
}
pub fn unsubscribe(&self, subs: &crate::SubscriptionList) -> Result<()> {
let rc = unsafe {
crate::ffi::blpapi_Session_unsubscribe(self.ptr, subs.as_ptr(), std::ptr::null(), 0)
};
if rc != 0 {
return Err(BlpError::Internal {
detail: format!("blpapi_Session_unsubscribe failed with rc={}", rc),
});
}
Ok(())
}
pub fn generate_authorized_identity_async(
&self,
auth_options: &crate::auth::AuthOptions,
cid: &CorrelationId,
) -> Result<()> {
let mut cid_ffi = cid.to_ffi();
let rc = unsafe {
let abstract_session = crate::ffi::blpapi_Session_getAbstractSession(self.ptr);
crate::ffi::blpapi_AbstractSession_generateAuthorizedIdentityAsync(
abstract_session,
auth_options.as_ptr(),
&mut cid_ffi,
)
};
if rc != 0 {
return Err(BlpError::Internal {
detail: format!("generateAuthorizedIdentityAsync failed with rc={rc}"),
});
}
Ok(())
}
pub fn authorized_identity(&self, cid: &CorrelationId) -> Result<crate::Identity> {
let cid_ffi = cid.to_ffi();
let mut identity_ptr: *mut crate::ffi::blpapi_Identity_t = std::ptr::null_mut();
let rc = unsafe {
let abstract_session = crate::ffi::blpapi_Session_getAbstractSession(self.ptr);
crate::ffi::blpapi_AbstractSession_getAuthorizedIdentity(
abstract_session,
&cid_ffi,
&mut identity_ptr,
)
};
if rc != 0 {
return Err(BlpError::Internal {
detail: format!("getAuthorizedIdentity failed with rc={rc}"),
});
}
crate::Identity::from_raw(identity_ptr)
}
pub fn generate_token(&self, cid: &CorrelationId) -> Result<()> {
let mut cid_ffi = cid.to_ffi();
let rc = unsafe {
crate::ffi::blpapi_Session_generateToken(self.ptr, &mut cid_ffi, std::ptr::null_mut())
};
if rc != 0 {
return Err(BlpError::Internal {
detail: format!("blpapi_Session_generateToken failed with rc={rc}"),
});
}
Ok(())
}
pub fn create_identity(&self) -> Result<crate::Identity> {
let identity_ptr = unsafe { crate::ffi::blpapi_Session_createIdentity(self.ptr) };
crate::Identity::from_raw(identity_ptr)
}
pub fn send_authorization_request(
&self,
request: &Request,
identity: &mut crate::Identity,
cid: &CorrelationId,
) -> Result<()> {
let mut cid_ffi = cid.to_ffi();
let rc = unsafe {
crate::ffi::blpapi_Session_sendAuthorizationRequest(
self.ptr,
request.as_ptr(),
identity.as_ptr(),
&mut cid_ffi,
std::ptr::null_mut(),
std::ptr::null(),
0,
)
};
if rc != 0 {
return Err(BlpError::Internal {
detail: format!("blpapi_Session_sendAuthorizationRequest failed with rc={rc}"),
});
}
Ok(())
}
}
impl Drop for AsyncSession {
fn drop(&mut self) {
if !self.ptr.is_null() {
unsafe {
crate::ffi::blpapi_Session_stop(self.ptr);
crate::ffi::blpapi_Session_destroy(self.ptr);
}
self.ptr = std::ptr::null_mut();
}
debug_assert!(self.handler.is_some() || self.ptr.is_null());
}
}