use crate::{ffi, DataType, HighPrecisionDatetime, Name};
use std::ffi::CStr;
use std::marker::PhantomData;
use std::mem::MaybeUninit;
use std::ptr::NonNull;
#[repr(transparent)]
pub struct Element<'a> {
ptr: *mut ffi::blpapi_Element_t,
_life: PhantomData<&'a ()>,
}
impl<'a> Element<'a> {
#[inline(always)]
pub(crate) fn new(ptr: *mut ffi::blpapi_Element_t) -> Self {
Self {
ptr,
_life: PhantomData,
}
}
#[inline(always)]
pub fn get(&self, name: &Name) -> Option<Element<'a>> {
let mut out = MaybeUninit::uninit();
let rc = unsafe {
ffi::blpapi_Element_getElement(
self.ptr,
out.as_mut_ptr(),
std::ptr::null(),
name.as_ptr(),
)
};
(rc == 0).then(|| Element::new(unsafe { out.assume_init() }))
}
#[inline(always)]
pub fn get_by_str(&self, name: &str) -> Option<Element<'a>> {
let interned = Name::get_or_intern(name);
self.get(&interned)
}
#[inline(always)]
pub fn get_at(&self, i: usize) -> Option<Element<'a>> {
let mut out = MaybeUninit::uninit();
let rc = unsafe { ffi::blpapi_Element_getElementAt(self.ptr, out.as_mut_ptr(), i) };
(rc == 0).then(|| Element::new(unsafe { out.assume_init() }))
}
#[inline(always)]
pub unsafe fn get_at_unchecked(&self, i: usize) -> Element<'a> {
let mut out = MaybeUninit::uninit();
ffi::blpapi_Element_getElementAt(self.ptr, out.as_mut_ptr(), i);
Element::new(out.assume_init())
}
#[inline(always)]
pub fn name(&self) -> Name {
let ptr = unsafe { ffi::blpapi_Element_name(self.ptr) };
unsafe { Name::from_raw(NonNull::new(ffi::blpapi_Name_duplicate(ptr)).unwrap()) }
}
#[inline(always)]
pub fn name_str(&self) -> &'a str {
let name = unsafe { ffi::blpapi_Element_name(self.ptr) };
let ptr = unsafe { ffi::blpapi_Name_string(name) };
unsafe { CStr::from_ptr(ptr) }
.to_str()
.expect("Bloomberg Element name contained invalid UTF-8")
}
#[inline(always)]
pub fn name_key(&self) -> usize {
unsafe { ffi::blpapi_Element_name(self.ptr) as usize }
}
#[inline(always)]
pub fn name_eq(&self, other: &Name) -> bool {
let ptr = unsafe { ffi::blpapi_Element_name(self.ptr) };
ptr == other.as_ptr()
}
#[inline(always)]
pub fn name_index(&self, names: &[Name]) -> Option<usize> {
let ptr = unsafe { ffi::blpapi_Element_name(self.ptr) };
names.iter().position(|name| ptr == name.as_ptr())
}
#[inline(always)]
pub fn datatype(&self) -> DataType {
DataType::from_raw(unsafe { ffi::blpapi_Element_datatype(self.ptr) })
}
#[inline(always)]
pub fn len(&self) -> usize {
unsafe { ffi::blpapi_Element_numValues(self.ptr) }
}
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline(always)]
pub fn num_children(&self) -> usize {
unsafe { ffi::blpapi_Element_numElements(self.ptr) }
}
#[inline(always)]
pub fn is_null(&self) -> bool {
unsafe { ffi::blpapi_Element_isNull(self.ptr) != 0 }
}
#[inline(always)]
pub fn is_array(&self) -> bool {
unsafe { ffi::blpapi_Element_isArray(self.ptr) != 0 }
}
#[must_use]
#[inline(always)]
pub fn get_f64(&self, i: usize) -> Option<f64> {
let mut v = MaybeUninit::uninit();
let rc = unsafe { ffi::blpapi_Element_getValueAsFloat64(self.ptr, v.as_mut_ptr(), i) };
(rc == 0).then(|| unsafe { v.assume_init() })
}
#[must_use]
#[inline(always)]
pub fn get_i64(&self, i: usize) -> Option<i64> {
let mut v = MaybeUninit::uninit();
let rc = unsafe { ffi::blpapi_Element_getValueAsInt64(self.ptr, v.as_mut_ptr(), i) };
(rc == 0).then(|| unsafe { v.assume_init() })
}
#[must_use]
#[inline(always)]
pub fn get_i32(&self, i: usize) -> Option<i32> {
let mut v = MaybeUninit::uninit();
let rc = unsafe { ffi::blpapi_Element_getValueAsInt32(self.ptr, v.as_mut_ptr(), i) };
(rc == 0).then(|| unsafe { v.assume_init() })
}
#[must_use]
#[inline(always)]
pub fn get_bool(&self, i: usize) -> Option<bool> {
let mut v = MaybeUninit::<i32>::uninit();
let rc = unsafe { ffi::blpapi_Element_getValueAsBool(self.ptr, v.as_mut_ptr(), i) };
(rc == 0).then(|| unsafe { v.assume_init() != 0 })
}
#[must_use]
#[inline(always)]
pub fn get_char(&self, i: usize) -> Option<u8> {
let mut v = MaybeUninit::<std::os::raw::c_char>::uninit();
let rc = unsafe { ffi::blpapi_Element_getValueAsChar(self.ptr, v.as_mut_ptr(), i) };
(rc == 0).then(|| unsafe { v.assume_init() } as u8)
}
#[must_use]
#[inline(always)]
pub fn get_str(&self, i: usize) -> Option<&'a str> {
let mut ptr = MaybeUninit::<*const i8>::uninit();
let rc = unsafe { ffi::blpapi_Element_getValueAsString(self.ptr, ptr.as_mut_ptr(), i) };
if rc == 0 {
let ptr = unsafe { ptr.assume_init() };
if ptr.is_null() {
return None;
}
unsafe { CStr::from_ptr(ptr) }.to_str().ok()
} else {
None
}
}
#[must_use]
#[inline(always)]
pub fn get_cstr(&self, i: usize) -> Option<&'a CStr> {
let mut ptr = MaybeUninit::<*const i8>::uninit();
let rc = unsafe { ffi::blpapi_Element_getValueAsString(self.ptr, ptr.as_mut_ptr(), i) };
if rc == 0 {
let ptr = unsafe { ptr.assume_init() };
if ptr.is_null() {
return None;
}
Some(unsafe { CStr::from_ptr(ptr) })
} else {
None
}
}
#[must_use]
#[inline(always)]
pub unsafe fn get_str_unchecked(&self, i: usize) -> Option<&'a str> {
let mut ptr = MaybeUninit::<*const i8>::uninit();
let rc = ffi::blpapi_Element_getValueAsString(self.ptr, ptr.as_mut_ptr(), i);
if rc == 0 {
let ptr = ptr.assume_init();
if ptr.is_null() {
return None;
}
let cstr = CStr::from_ptr(ptr);
Some(std::str::from_utf8_unchecked(cstr.to_bytes()))
} else {
None
}
}
#[must_use]
#[inline(always)]
pub fn get_datetime(&self, i: usize) -> Option<HighPrecisionDatetime> {
let mut v = MaybeUninit::uninit();
let rc = unsafe {
ffi::blpapi_Element_getValueAsHighPrecisionDatetime(self.ptr, v.as_mut_ptr(), i)
};
(rc == 0).then(|| HighPrecisionDatetime(unsafe { v.assume_init() }))
}
#[must_use]
#[inline(always)]
pub fn get_timestamp_us(&self, i: usize) -> Option<i64> {
self.get_datetime(i).map(|dt| dt.to_micros())
}
#[inline(always)]
pub fn get_element(&self, i: usize) -> Option<Element<'a>> {
let mut out = MaybeUninit::uninit();
let rc = unsafe { ffi::blpapi_Element_getValueAsElement(self.ptr, out.as_mut_ptr(), i) };
(rc == 0).then(|| Element::new(unsafe { out.assume_init() }))
}
#[inline]
pub fn children(&'a self) -> ChildrenIter<'a> {
ChildrenIter {
elem: self,
idx: 0,
len: self.num_children(),
}
}
#[inline]
pub fn values(&'a self) -> ValuesIter<'a> {
ValuesIter {
elem: self,
idx: 0,
len: self.len(),
}
}
#[inline(always)]
pub(crate) fn as_ptr(&self) -> *mut ffi::blpapi_Element_t {
self.ptr
}
#[inline]
pub fn get_value(&self, i: usize) -> Option<crate::Value<'a>> {
use crate::{DataType, Value};
if self.is_null() {
return Some(Value::Null);
}
if i >= self.len() {
return None;
}
match self.datatype() {
DataType::Bool => self.get_bool(i).map(Value::Bool),
DataType::Char | DataType::Byte => {
self.get_char(i).map(|c| match c {
b'Y' => Value::Bool(true),
b'N' => Value::Bool(false),
other => Value::Byte(other),
})
}
DataType::Int32 => self.get_i32(i).map(Value::Int32),
DataType::Int64 => self.get_i64(i).map(Value::Int64),
DataType::Float32 | DataType::Float64 | DataType::Decimal => {
self.get_f64(i).map(Value::Float64)
}
DataType::String => self.get_str(i).map(Value::String),
DataType::Date => {
self.get_datetime(i).map(|dt| {
let micros = dt.to_micros();
let days = (micros / 86_400_000_000) as i32;
Value::Date32(days)
})
}
DataType::Time => {
self.get_datetime(i)
.map(|dt| Value::Time64Micros(dt.to_time_micros()))
}
DataType::Datetime => self.get_datetime(i).map(|dt| {
if dt.has_date_parts() {
Value::TimestampMicros(dt.to_micros())
} else {
Value::Time64Micros(dt.to_time_micros())
}
}),
DataType::Enumeration => {
self.get_str(i).map(Value::Enum)
}
DataType::Sequence | DataType::Choice => {
Some(Value::Null)
}
DataType::ByteArray | DataType::CorrelationId => {
Some(Value::Null)
}
}
}
#[inline(always)]
pub fn get_value_fast(&self, i: usize) -> Option<crate::Value<'a>> {
self.get_value_fast_with_datatype(i, self.datatype())
}
#[inline(always)]
pub fn get_value_fast_with_datatype(
&self,
i: usize,
datatype: crate::DataType,
) -> Option<crate::Value<'a>> {
use crate::{DataType, Value};
match datatype {
DataType::Bool => self.get_bool(i).map(Value::Bool),
DataType::Char | DataType::Byte => self.get_char(i).map(|c| match c {
b'Y' => Value::Bool(true),
b'N' => Value::Bool(false),
other => Value::Byte(other),
}),
DataType::Int32 => self.get_i32(i).map(Value::Int32),
DataType::Int64 => self.get_i64(i).map(Value::Int64),
DataType::Float32 | DataType::Float64 | DataType::Decimal => {
self.get_f64(i).map(Value::Float64)
}
DataType::String => self.get_str(i).map(Value::String),
DataType::Date => self.get_datetime(i).map(|dt| {
let micros = dt.to_micros();
Value::Date32((micros / 86_400_000_000) as i32)
}),
DataType::Time => self
.get_datetime(i)
.map(|dt| Value::Time64Micros(dt.to_time_micros())),
DataType::Datetime => self.get_datetime(i).map(|dt| {
if dt.has_date_parts() {
Value::TimestampMicros(dt.to_micros())
} else {
Value::Time64Micros(dt.to_time_micros())
}
}),
DataType::Enumeration => self.get_str(i).map(Value::Enum),
DataType::Sequence
| DataType::Choice
| DataType::ByteArray
| DataType::CorrelationId => Some(Value::Null),
}
}
#[must_use]
#[inline(always)]
pub fn get_date32(&self, i: usize) -> Option<i32> {
self.get_datetime(i).map(|dt| {
let micros = dt.to_micros();
(micros / 86_400_000_000) as i32
})
}
}
pub struct ChildrenIter<'a> {
elem: &'a Element<'a>,
idx: usize,
len: usize,
}
impl<'a> Iterator for ChildrenIter<'a> {
type Item = Element<'a>;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
if self.idx < self.len {
let i = self.idx;
self.idx += 1;
let item = self.elem.get_at(i);
if item.is_none() {
self.idx = self.len;
}
item
} else {
None
}
}
#[inline(always)]
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.len - self.idx;
(remaining, Some(remaining))
}
}
impl<'a> ExactSizeIterator for ChildrenIter<'a> {}
pub struct ValuesIter<'a> {
elem: &'a Element<'a>,
idx: usize,
len: usize,
}
impl<'a> Iterator for ValuesIter<'a> {
type Item = Element<'a>;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
if self.idx < self.len {
let i = self.idx;
self.idx += 1;
let item = self.elem.get_element(i);
if item.is_none() {
self.idx = self.len;
}
item
} else {
None
}
}
#[inline(always)]
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.len - self.idx;
(remaining, Some(remaining))
}
}
impl<'a> ExactSizeIterator for ValuesIter<'a> {}
impl<'a> std::fmt::Debug for Element<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Element")
.field("name", &self.name().as_str())
.field("datatype", &self.datatype())
.field("len", &self.len())
.field("is_null", &self.is_null())
.finish()
}
}