use std::ffi::{CStr, CString};
use std::marker::PhantomData;
use std::rc::Rc;
use crate::errors::{BlpError, Result};
use crate::request::Request;
use crate::schema::Operation;
pub struct Service<'session> {
ptr: *mut crate::ffi::blpapi_Service_t,
_session: PhantomData<&'session crate::session::Session>,
_not_send_sync: PhantomData<Rc<()>>,
}
impl<'session> Service<'session> {
pub(crate) fn from_raw(ptr: *mut crate::ffi::blpapi_Service_t) -> Result<Self> {
if ptr.is_null() {
return Err(BlpError::Internal {
detail: "null service pointer".into(),
});
}
Ok(Self {
ptr,
_session: PhantomData,
_not_send_sync: PhantomData,
})
}
#[allow(dead_code)] pub(crate) fn as_ptr(&self) -> *mut crate::ffi::blpapi_Service_t {
self.ptr
}
pub fn create_request(&self, operation: &str) -> Result<Request> {
let c_operation = CString::new(operation).map_err(|e| BlpError::InvalidArgument {
detail: format!("invalid operation name: {}", e),
})?;
let mut req_ptr: *mut crate::ffi::blpapi_Request_t = std::ptr::null_mut();
let rc = unsafe {
crate::ffi::blpapi_Service_createRequest(self.ptr, &mut req_ptr, c_operation.as_ptr())
};
if rc != 0 {
return Err(BlpError::Internal {
detail: format!("blpapi_Service_createRequest failed with rc={}", rc),
});
}
Request::from_raw(req_ptr)
}
pub fn name(&self) -> &str {
unsafe {
let name_ptr = crate::ffi::blpapi_Service_name(self.ptr);
if name_ptr.is_null() {
return "";
}
CStr::from_ptr(name_ptr).to_str().unwrap_or("")
}
}
pub fn description(&self) -> &str {
unsafe {
let desc_ptr = crate::ffi::blpapi_Service_description(self.ptr);
if desc_ptr.is_null() {
return "";
}
CStr::from_ptr(desc_ptr).to_str().unwrap_or("")
}
}
pub fn num_operations(&self) -> usize {
let count = unsafe { crate::ffi::blpapi_Service_numOperations(self.ptr) };
count.max(0) as usize
}
pub fn get_operation_at(&self, index: usize) -> Result<Operation<'session>> {
if index >= self.num_operations() {
return Err(BlpError::InvalidArgument {
detail: format!(
"Operation index {} out of bounds (service has {} operations)",
index,
self.num_operations()
),
});
}
let mut op_ptr: *mut crate::ffi::blpapi_Operation_t = std::ptr::null_mut();
let rc = unsafe { crate::ffi::blpapi_Service_getOperationAt(self.ptr, &mut op_ptr, index) };
if rc != 0 || op_ptr.is_null() {
return Err(BlpError::Internal {
detail: format!("Failed to get operation at index {}, rc={}", index, rc),
});
}
unsafe { Operation::from_raw(op_ptr) }.ok_or_else(|| BlpError::Internal {
detail: "Received null operation pointer".into(),
})
}
pub fn operations(&self) -> OperationIter<'_, 'session> {
OperationIter {
service: self,
index: 0,
count: self.num_operations(),
}
}
}
pub struct OperationIter<'service, 'session> {
service: &'service Service<'session>,
index: usize,
count: usize,
}
impl<'service, 'session> Iterator for OperationIter<'service, 'session> {
type Item = Operation<'session>;
fn next(&mut self) -> Option<Self::Item> {
if self.index >= self.count {
return None;
}
let op = self.service.get_operation_at(self.index).ok();
self.index += 1;
op
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.count - self.index;
(remaining, Some(remaining))
}
}
impl ExactSizeIterator for OperationIter<'_, '_> {}