1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
//! Identity for authenticated Bloomberg sessions
use crate::errors::{BlpError, Result};
/// Identity handle for authenticated Bloomberg sessions.
///
/// Identities are created by the session and used for authorization.
/// Required for accessing permissioned data or services.
///
/// # Examples
///
/// ```ignore
/// // Generate token and authorize
/// let identity = session.generate_token()?;
/// session.authorize(&identity)?;
///
/// // Use identity for permissioned requests
/// session.send_request(&req, Some(&identity), None)?;
/// ```
///
/// # Lifecycle
/// The identity is a reference-counted Bloomberg handle. The local SDK headers
/// do not document cross-thread use, so this wrapper is not `Send` or `Sync`.
pub struct Identity {
ptr: *mut crate::ffi::blpapi_Identity_t,
}
impl Identity {
/// Create an Identity from a raw pointer (internal use only)
pub(crate) fn from_raw(ptr: *mut crate::ffi::blpapi_Identity_t) -> Result<Self> {
if ptr.is_null() {
return Err(BlpError::Internal {
detail: "null identity pointer".into(),
});
}
Ok(Self { ptr })
}
pub(crate) fn as_ptr(&self) -> *mut crate::ffi::blpapi_Identity_t {
self.ptr
}
pub fn is_authorized(&self, service: &crate::Service<'_>) -> bool {
let rc = unsafe { crate::ffi::blpapi_Identity_isAuthorized(self.ptr, service.as_ptr()) };
rc != 0
}
/// Return whether this identity is entitled to ALL of the given
/// entitlement IDs for `service`.
///
/// Mirrors the C++ `Identity::hasEntitlements` contract: the FFI return
/// value is the boolean answer (nonzero = entitled), not an error code.
/// The `failedEntitlements` out-array is optional and we don't request it;
/// its count parameter is in/out (input = array capacity), so it must be 0
/// when no array is supplied.
pub fn has_entitlements(&self, service: &crate::Service<'_>, eids: &[i32]) -> Result<bool> {
let mut failed_count: i32 = 0;
let rc = unsafe {
crate::ffi::blpapi_Identity_hasEntitlements(
self.ptr,
service.as_ptr(),
std::ptr::null(),
eids.as_ptr(),
eids.len(),
std::ptr::null_mut(),
&mut failed_count,
)
};
Ok(rc != 0)
}
/// Check entitlements for `service`, reporting which EIDs failed.
///
/// Same FFI contract as [`Identity::has_entitlements`], but supplies the
/// optional `failedEntitlements` out-array (capacity = `eids.len()`; the
/// count parameter is in/out: capacity on input, failures written on
/// output — blpapi_identity.h:196-216). When the identity is entitled to
/// every EID the array is untouched and `failed_eids` is empty.
pub fn check_entitlements(
&self,
service: &crate::Service<'_>,
eids: &[i32],
) -> Result<EntitlementCheck> {
let mut failed = vec![0_i32; eids.len()];
let mut failed_count: i32 = eids.len() as i32;
let rc = unsafe {
crate::ffi::blpapi_Identity_hasEntitlements(
self.ptr,
service.as_ptr(),
std::ptr::null(),
eids.as_ptr(),
eids.len(),
failed.as_mut_ptr(),
&mut failed_count,
)
};
let entitled = rc != 0;
let failed_eids = if entitled {
Vec::new()
} else {
let count = usize::try_from(failed_count).unwrap_or(0).min(failed.len());
failed.truncate(count);
failed
};
Ok(EntitlementCheck {
entitled,
failed_eids,
})
}
pub fn seat_type(&self) -> Result<SeatType> {
let mut raw: i32 = -1;
let rc = unsafe { crate::ffi::blpapi_Identity_getSeatType(self.ptr, &mut raw) };
if rc != 0 {
return Err(BlpError::Internal {
detail: format!("getSeatType failed: rc={rc}"),
});
}
Ok(SeatType::from_raw(raw))
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SeatType {
Bps,
NonBps,
Invalid,
}
impl SeatType {
fn from_raw(raw: i32) -> Self {
match raw {
0 => Self::Bps,
1 => Self::NonBps,
_ => Self::Invalid,
}
}
pub fn as_str(self) -> &'static str {
match self {
Self::Bps => "BPS",
Self::NonBps => "NONBPS",
Self::Invalid => "INVALID",
}
}
}
/// Result of an entitlement check against a service.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct EntitlementCheck {
/// Whether the identity is entitled to every requested EID.
pub entitled: bool,
/// The subset of requested EIDs the identity is NOT entitled to.
/// Empty when `entitled` is true.
pub failed_eids: Vec<i32>,
}
impl Drop for Identity {
fn drop(&mut self) {
if !self.ptr.is_null() {
// SAFETY: `blpapi_Session_createIdentity` hands the caller an owned
// reference (the C++ `Identity(handle)` ctor takes ownership without
// addRef and its destructor calls release; blpapi_identity.h:279-290).
// Releasing exactly once here balances that reference.
unsafe { crate::ffi::blpapi_Identity_release(self.ptr) };
self.ptr = std::ptr::null_mut();
}
}
}