use crate::models::quote::FormattedValue;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Deserialize)]
struct RawIndustryResponse {
data: RawIndustryData,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
struct RawIndustryData {
name: String,
symbol: Option<String>,
key: String,
sector_name: Option<String>,
sector_key: Option<String>,
overview: Option<RawOverview>,
performance: Option<RawPerformance>,
#[serde(default)]
performance_overview_benchmark: Option<RawBenchmarkPerformance>,
#[serde(default)]
top_companies: Vec<RawCompany>,
#[serde(default)]
top_performing_companies: Vec<RawPerformingCompany>,
#[serde(default)]
top_growth_companies: Vec<RawGrowthCompany>,
#[serde(default)]
research_reports: Vec<RawResearchReport>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
struct RawOverview {
companies_count: Option<i64>,
market_cap: Option<FormattedValue<f64>>,
description: Option<String>,
market_weight: Option<FormattedValue<f64>>,
employee_count: Option<FormattedValue<i64>>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
struct RawPerformance {
ytd_change_percent: Option<FormattedValue<f64>>,
reg_market_change_percent: Option<FormattedValue<f64>>,
three_year_change_percent: Option<FormattedValue<f64>>,
one_year_change_percent: Option<FormattedValue<f64>>,
five_year_change_percent: Option<FormattedValue<f64>>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
struct RawBenchmarkPerformance {
name: Option<String>,
ytd_change_percent: Option<FormattedValue<f64>>,
reg_market_change_percent: Option<FormattedValue<f64>>,
three_year_change_percent: Option<FormattedValue<f64>>,
one_year_change_percent: Option<FormattedValue<f64>>,
five_year_change_percent: Option<FormattedValue<f64>>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
struct RawCompany {
symbol: String,
name: Option<String>,
last_price: Option<FormattedValue<f64>>,
market_cap: Option<FormattedValue<f64>>,
market_weight: Option<FormattedValue<f64>>,
#[serde(rename = "regMarketChangePercent")]
day_change_percent: Option<FormattedValue<f64>>,
ytd_return: Option<FormattedValue<f64>>,
rating: Option<String>,
target_price: Option<FormattedValue<f64>>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
struct RawPerformingCompany {
symbol: String,
name: Option<String>,
last_price: Option<FormattedValue<f64>>,
ytd_return: Option<FormattedValue<f64>>,
target_price: Option<FormattedValue<f64>>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
struct RawGrowthCompany {
symbol: String,
name: Option<String>,
last_price: Option<FormattedValue<f64>>,
ytd_return: Option<FormattedValue<f64>>,
growth_estimate: Option<FormattedValue<f64>>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
struct RawResearchReport {
id: Option<String>,
#[serde(rename = "reportTitle")]
title: Option<String>,
provider: Option<String>,
report_date: Option<String>,
report_type: Option<String>,
investment_rating: Option<String>,
target_price: Option<f64>,
target_price_status: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct IndustryData {
pub name: String,
pub key: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub symbol: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sector_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sector_key: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub overview: Option<IndustryOverview>,
#[serde(skip_serializing_if = "Option::is_none")]
pub performance: Option<IndustryPerformance>,
#[serde(skip_serializing_if = "Option::is_none")]
pub benchmark: Option<BenchmarkPerformance>,
#[serde(default)]
pub top_companies: Vec<IndustryCompany>,
#[serde(default)]
pub top_performing_companies: Vec<PerformingCompany>,
#[serde(default)]
pub top_growth_companies: Vec<GrowthCompany>,
#[serde(default)]
pub research_reports: Vec<ResearchReport>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "dataframe", derive(crate::ToDataFrame))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct IndustryOverview {
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub companies_count: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub market_cap: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub market_weight: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub employee_count: Option<i64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "dataframe", derive(crate::ToDataFrame))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct IndustryPerformance {
#[serde(skip_serializing_if = "Option::is_none")]
pub day_change_percent: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ytd_change_percent: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub one_year_change_percent: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub three_year_change_percent: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub five_year_change_percent: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "dataframe", derive(crate::ToDataFrame))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct BenchmarkPerformance {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub day_change_percent: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ytd_change_percent: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub one_year_change_percent: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub three_year_change_percent: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub five_year_change_percent: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "dataframe", derive(crate::ToDataFrame))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct IndustryCompany {
pub symbol: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_price: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub market_cap: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub market_weight: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub day_change_percent: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ytd_return: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rating: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub target_price: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "dataframe", derive(crate::ToDataFrame))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct PerformingCompany {
pub symbol: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_price: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ytd_return: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub target_price: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "dataframe", derive(crate::ToDataFrame))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct GrowthCompany {
pub symbol: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_price: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ytd_return: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub growth_estimate: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "dataframe", derive(crate::ToDataFrame))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct ResearchReport {
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub provider: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub report_date: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub report_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub investment_rating: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub target_price: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub target_price_status: Option<String>,
}
impl IndustryData {
pub(crate) fn from_response(json: &serde_json::Value) -> Result<Self, String> {
let raw: RawIndustryResponse =
serde_json::from_value(json.clone()).map_err(|e| e.to_string())?;
let data = raw.data;
Ok(IndustryData {
name: data.name,
key: data.key,
symbol: data.symbol,
sector_name: data.sector_name,
sector_key: data.sector_key,
overview: data.overview.map(|o| IndustryOverview {
description: o.description,
companies_count: o.companies_count,
market_cap: o.market_cap.and_then(|v| v.raw),
market_weight: o.market_weight.and_then(|v| v.raw),
employee_count: o.employee_count.and_then(|v| v.raw),
}),
performance: data.performance.map(|p| IndustryPerformance {
day_change_percent: p.reg_market_change_percent.and_then(|v| v.raw),
ytd_change_percent: p.ytd_change_percent.and_then(|v| v.raw),
one_year_change_percent: p.one_year_change_percent.and_then(|v| v.raw),
three_year_change_percent: p.three_year_change_percent.and_then(|v| v.raw),
five_year_change_percent: p.five_year_change_percent.and_then(|v| v.raw),
}),
benchmark: data
.performance_overview_benchmark
.map(|b| BenchmarkPerformance {
name: b.name,
day_change_percent: b.reg_market_change_percent.and_then(|v| v.raw),
ytd_change_percent: b.ytd_change_percent.and_then(|v| v.raw),
one_year_change_percent: b.one_year_change_percent.and_then(|v| v.raw),
three_year_change_percent: b.three_year_change_percent.and_then(|v| v.raw),
five_year_change_percent: b.five_year_change_percent.and_then(|v| v.raw),
}),
top_companies: data
.top_companies
.into_iter()
.map(|c| IndustryCompany {
symbol: c.symbol,
name: c.name,
last_price: c.last_price.and_then(|v| v.raw),
market_cap: c.market_cap.and_then(|v| v.raw),
market_weight: c.market_weight.and_then(|v| v.raw),
day_change_percent: c.day_change_percent.and_then(|v| v.raw),
ytd_return: c.ytd_return.and_then(|v| v.raw),
rating: c.rating,
target_price: c.target_price.and_then(|v| v.raw),
})
.collect(),
top_performing_companies: data
.top_performing_companies
.into_iter()
.map(|c| PerformingCompany {
symbol: c.symbol,
name: c.name,
last_price: c.last_price.and_then(|v| v.raw),
ytd_return: c.ytd_return.and_then(|v| v.raw),
target_price: c.target_price.and_then(|v| v.raw),
})
.collect(),
top_growth_companies: data
.top_growth_companies
.into_iter()
.map(|c| GrowthCompany {
symbol: c.symbol,
name: c.name,
last_price: c.last_price.and_then(|v| v.raw),
ytd_return: c.ytd_return.and_then(|v| v.raw),
growth_estimate: c.growth_estimate.and_then(|v| v.raw),
})
.collect(),
research_reports: data
.research_reports
.into_iter()
.map(|r| ResearchReport {
id: r.id,
title: r.title,
provider: r.provider,
report_date: r.report_date,
report_type: r.report_type,
investment_rating: r.investment_rating,
target_price: r.target_price,
target_price_status: r.target_price_status,
})
.collect(),
})
}
}