use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ElementInfo {
pub name: String,
pub description: String,
pub data_type: String,
pub type_name: String,
pub is_array: bool,
pub is_optional: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub enum_values: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub children: Vec<ElementInfo>,
}
impl ElementInfo {
pub fn empty() -> Self {
Self {
name: String::new(),
description: String::new(),
data_type: String::new(),
type_name: String::new(),
is_array: false,
is_optional: false,
enum_values: None,
children: Vec::new(),
}
}
pub fn all_element_names(&self) -> Vec<String> {
let mut names = Vec::new();
for child in &self.children {
names.push(child.name.clone());
names.extend(child.all_element_names());
}
names
}
pub fn find_enum_values(&self, element_name: &str) -> Option<Vec<String>> {
if self.name == element_name {
return self.enum_values.clone();
}
for child in &self.children {
if let Some(values) = child.find_enum_values(element_name) {
return Some(values);
}
}
None
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct OperationSchema {
pub name: String,
pub description: String,
pub request: ElementInfo,
#[serde(default)]
pub responses: Vec<ElementInfo>,
}
impl OperationSchema {
pub fn request_element_names(&self) -> Vec<String> {
self.request.all_element_names()
}
pub fn find_request_enum_values(&self, element_name: &str) -> Option<Vec<String>> {
self.request.find_enum_values(element_name)
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ServiceSchema {
pub service: String,
pub description: String,
pub operations: Vec<OperationSchema>,
pub cached_at: String,
}
impl ServiceSchema {
pub fn new(service: String, description: String, operations: Vec<OperationSchema>) -> Self {
Self {
service,
description,
operations,
cached_at: chrono::Utc::now().to_rfc3339(),
}
}
pub fn get_operation(&self, name: &str) -> Option<&OperationSchema> {
self.operations.iter().find(|op| op.name == name)
}
pub fn operation_names(&self) -> Vec<String> {
self.operations.iter().map(|op| op.name.clone()).collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_element_info_serialization() {
let elem = ElementInfo {
name: "securities".to_string(),
description: "List of securities".to_string(),
data_type: "String".to_string(),
type_name: "SecuritiesArray".to_string(),
is_array: true,
is_optional: false,
enum_values: None,
children: vec![],
};
let json = serde_json::to_string(&elem).unwrap();
assert!(json.contains("\"name\":\"securities\""));
assert!(json.contains("\"is_array\":true"));
assert!(!json.contains("enum_values"));
}
#[test]
fn test_element_with_enum() {
let elem = ElementInfo {
name: "periodicitySelection".to_string(),
description: "Periodicity".to_string(),
data_type: "Enumeration".to_string(),
type_name: "PeriodicityEnum".to_string(),
is_array: false,
is_optional: true,
enum_values: Some(vec!["DAILY".to_string(), "WEEKLY".to_string()]),
children: vec![],
};
let json = serde_json::to_string(&elem).unwrap();
assert!(json.contains("\"enum_values\":[\"DAILY\",\"WEEKLY\"]"));
}
#[test]
fn test_find_enum_values() {
let child = ElementInfo {
name: "periodicitySelection".to_string(),
description: String::new(),
data_type: "Enumeration".to_string(),
type_name: String::new(),
is_array: false,
is_optional: true,
enum_values: Some(vec!["DAILY".to_string(), "WEEKLY".to_string()]),
children: vec![],
};
let parent = ElementInfo {
name: "request".to_string(),
description: String::new(),
data_type: "Sequence".to_string(),
type_name: String::new(),
is_array: false,
is_optional: false,
enum_values: None,
children: vec![child],
};
let values = parent.find_enum_values("periodicitySelection");
assert_eq!(
values,
Some(vec!["DAILY".to_string(), "WEEKLY".to_string()])
);
let not_found = parent.find_enum_values("nonexistent");
assert_eq!(not_found, None);
}
#[test]
fn test_service_schema() {
let op = OperationSchema {
name: "ReferenceDataRequest".to_string(),
description: "Get reference data".to_string(),
request: ElementInfo::empty(),
responses: vec![],
};
let schema = ServiceSchema::new(
"//blp/refdata".to_string(),
"Reference Data Service".to_string(),
vec![op],
);
assert_eq!(schema.operation_names(), vec!["ReferenceDataRequest"]);
assert!(schema.get_operation("ReferenceDataRequest").is_some());
assert!(schema.get_operation("NonExistent").is_none());
}
}