use crate::error::{OpenFIGIError, Result};
use crate::model::response::common::FigiResult;
use serde::{Deserialize, Serialize};
#[derive(Debug)]
pub struct MappingResponses(Vec<Result<MappingData>>);
impl MappingResponses {
#[doc(hidden)]
pub(crate) fn new(results: Vec<Result<MappingData>>) -> Self {
Self(results)
}
pub fn successes(&self) -> impl Iterator<Item = (usize, &MappingData)> {
self.0
.iter()
.enumerate()
.filter_map(|(i, r)| r.as_ref().ok().map(|data| (i, data)))
}
pub fn failures(&self) -> impl Iterator<Item = (usize, &OpenFIGIError)> {
self.0
.iter()
.enumerate()
.filter_map(|(i, r)| r.as_ref().err().map(|err| (i, err)))
}
#[must_use]
pub fn len(&self) -> usize {
self.0.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn as_slice(&self) -> &[Result<MappingData>] {
&self.0
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct MappingData {
pub data: Vec<FigiResult>,
}
impl MappingData {
#[must_use]
pub fn data(&self) -> &[FigiResult] {
&self.data
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{model::response::common::ResponseResult, test_utils::load_test_data};
fn from_response_results(raw: Vec<ResponseResult<MappingData>>) -> MappingResponses {
MappingResponses::new(
raw.into_iter()
.map(|res| match res {
ResponseResult::Success(data) => Ok(data),
ResponseResult::Error(err) => Err(OpenFIGIError::response_error(
reqwest::StatusCode::OK,
err.error,
String::new(),
)),
})
.collect(),
)
}
#[test]
fn test_deserialize_isin_example() {
let json_str = load_test_data("mapping", "isin_example.json");
let raw: Vec<ResponseResult<MappingData>> = serde_json::from_str(&json_str).unwrap();
let mapping_response = from_response_results(raw);
assert_eq!(mapping_response.len(), 1);
let response_result = &mapping_response.as_slice()[0];
match response_result {
Ok(mapping_data) => {
let figi_result = mapping_data.data();
assert!(!figi_result.is_empty());
let first_entry = &figi_result[0];
assert_eq!(first_entry.figi, "BBG000BLNNH6");
assert_eq!(first_entry.ticker, Some("IBM".to_string()));
assert_eq!(first_entry.display_name(), "INTL BUSINESS MACHINES CORP");
assert!(first_entry.has_composite_figi());
assert!(first_entry.has_share_class_figi());
assert_eq!(first_entry.composite_figi, Some("BBG000BLNNH6".to_string()));
assert_eq!(
first_entry.share_class_figi,
Some("BBG001S5S399".to_string())
);
}
Err(e) => panic!("Expected success, got error: {e}"),
}
}
#[test]
fn test_deserialize_invalid_identifier() {
let json_str = load_test_data("mapping", "invalid_identifier.json");
let raw: Vec<ResponseResult<MappingData>> = serde_json::from_str(&json_str).unwrap();
let mapping_response = from_response_results(raw);
assert_eq!(mapping_response.len(), 1);
let response_result = &mapping_response.as_slice()[0];
match response_result {
Ok(_) => panic!("Expected error, got success"),
Err(OpenFIGIError::ResponseError(resp)) => {
assert!(resp.message.contains("Invalid idValue format."));
}
Err(e) => panic!("Unexpected error variant: {e}"),
}
}
#[test]
fn test_deserialize_bulk_request() {
let json_str = load_test_data("mapping", "bulk_request.json");
let raw: Vec<ResponseResult<MappingData>> = serde_json::from_str(&json_str).unwrap();
let mapping_response = from_response_results(raw);
assert_eq!(mapping_response.len(), 2);
let ibm_response_result = &mapping_response.as_slice()[0];
match ibm_response_result {
Ok(mapping_data) => {
let ibm_figi_result = mapping_data.data();
assert!(!ibm_figi_result.is_empty());
assert_eq!(ibm_figi_result[0].ticker, Some("IBM".to_string()));
}
Err(e) => panic!("Expected IBM success, got error: {e}"),
}
let aapl_response_result = &mapping_response.as_slice()[1];
match aapl_response_result {
Ok(mapping_data) => {
let aapl_figi_result = mapping_data.data();
assert!(!aapl_figi_result.is_empty());
assert_eq!(aapl_figi_result[0].ticker, Some("AAPL".to_string()));
}
Err(e) => panic!("Expected AAPL success, got error: {e}"),
}
}
#[test]
fn test_deserialize_cusip_with_exchange() {
let json_str = load_test_data("mapping", "cusip_with_exchange.json");
let raw: Vec<ResponseResult<MappingData>> = serde_json::from_str(&json_str).unwrap();
let mapping_response = from_response_results(raw);
assert_eq!(mapping_response.len(), 1);
let response_result = &mapping_response.as_slice()[0];
match response_result {
Ok(mapping_data) => {
let figi_result = mapping_data.data();
assert!(!figi_result.is_empty());
for data in figi_result {
assert!(!data.figi.is_empty());
assert!(data.ticker.is_some());
}
}
Err(e) => panic!("Expected success, got error: {e}"),
}
}
#[test]
fn test_deserialize_ticker_with_security_type() {
let json_str = load_test_data("mapping", "ticker_with_security_type.json");
let raw: Vec<ResponseResult<MappingData>> = serde_json::from_str(&json_str).unwrap();
let mapping_response = from_response_results(raw);
assert_eq!(mapping_response.len(), 1);
let response_result = &mapping_response.as_slice()[0];
match response_result {
Ok(mapping_data) => {
let figi_result = mapping_data.data();
assert!(!figi_result.is_empty());
for data in figi_result {
assert!(data.security_type.is_some());
assert!(data.market_sector.is_some());
}
}
Err(e) => panic!("Expected success, got error: {e}"),
}
}
#[test]
fn test_deserialize_option_example() {
let json_str = load_test_data("mapping", "option_example.json");
let raw: Vec<ResponseResult<MappingData>> = serde_json::from_str(&json_str).unwrap();
let mapping_response = from_response_results(raw);
assert_eq!(mapping_response.len(), 1);
let response_result = &mapping_response.as_slice()[0];
match response_result {
Ok(mapping_data) => {
let figi_result = mapping_data.data();
for data in figi_result {
assert!(!data.figi.is_empty());
}
}
Err(e) => {
assert!(!e.to_string().is_empty());
}
}
}
#[test]
fn test_deserialize_currency_mic_example() {
let json_str = load_test_data("mapping", "currency_mic_example.json");
let raw: Vec<ResponseResult<MappingData>> = serde_json::from_str(&json_str).unwrap();
let mapping_response = from_response_results(raw);
assert_eq!(mapping_response.len(), 1);
let response_result = &mapping_response.as_slice()[0];
match response_result {
Ok(mapping_data) => {
let figi_result = mapping_data.data();
for data in figi_result {
assert!(!data.figi.is_empty());
}
}
Err(e) => {
assert!(!e.to_string().is_empty());
}
}
}
#[test]
fn test_figi_result_display_name_fallback() {
let figi_with_ticker = FigiResult {
figi: "BBG000BLNNH6".to_string(),
name: None,
ticker: Some("IBM".to_string()),
security_type: None,
market_sector: None,
exch_code: None,
share_class_figi: None,
composite_figi: None,
security_type2: None,
security_description: None,
metadata: None,
};
assert_eq!(figi_with_ticker.display_name(), "IBM");
let figi_only = FigiResult {
figi: "BBG000BLNNH6".to_string(),
name: None,
ticker: None,
security_type: None,
market_sector: None,
exch_code: None,
share_class_figi: None,
composite_figi: None,
security_type2: None,
security_description: None,
metadata: None,
};
assert_eq!(figi_only.display_name(), "BBG000BLNNH6");
}
}