1use crate::models::quote::FormattedValue;
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Clone, Deserialize)]
9struct RawIndustryResponse {
10 data: RawIndustryData,
11}
12
13#[derive(Debug, Clone, Deserialize)]
14#[serde(rename_all = "camelCase")]
15struct RawIndustryData {
16 name: String,
17 symbol: Option<String>,
18 key: String,
19 sector_name: Option<String>,
20 sector_key: Option<String>,
21 overview: Option<RawOverview>,
22 performance: Option<RawPerformance>,
23 #[serde(default)]
24 performance_overview_benchmark: Option<RawBenchmarkPerformance>,
25 #[serde(default)]
26 top_companies: Vec<RawCompany>,
27 #[serde(default)]
28 top_performing_companies: Vec<RawPerformingCompany>,
29 #[serde(default)]
30 top_growth_companies: Vec<RawGrowthCompany>,
31 #[serde(default)]
32 research_reports: Vec<RawResearchReport>,
33}
34
35#[derive(Debug, Clone, Deserialize)]
36#[serde(rename_all = "camelCase")]
37struct RawOverview {
38 companies_count: Option<i64>,
39 market_cap: Option<FormattedValue<f64>>,
40 description: Option<String>,
41 market_weight: Option<FormattedValue<f64>>,
42 employee_count: Option<FormattedValue<i64>>,
43}
44
45#[derive(Debug, Clone, Deserialize)]
46#[serde(rename_all = "camelCase")]
47struct RawPerformance {
48 ytd_change_percent: Option<FormattedValue<f64>>,
49 reg_market_change_percent: Option<FormattedValue<f64>>,
50 three_year_change_percent: Option<FormattedValue<f64>>,
51 one_year_change_percent: Option<FormattedValue<f64>>,
52 five_year_change_percent: Option<FormattedValue<f64>>,
53}
54
55#[derive(Debug, Clone, Deserialize)]
56#[serde(rename_all = "camelCase")]
57struct RawBenchmarkPerformance {
58 name: Option<String>,
59 ytd_change_percent: Option<FormattedValue<f64>>,
60 reg_market_change_percent: Option<FormattedValue<f64>>,
61 three_year_change_percent: Option<FormattedValue<f64>>,
62 one_year_change_percent: Option<FormattedValue<f64>>,
63 five_year_change_percent: Option<FormattedValue<f64>>,
64}
65
66#[derive(Debug, Clone, Deserialize)]
67#[serde(rename_all = "camelCase")]
68struct RawCompany {
69 symbol: String,
70 name: Option<String>,
71 last_price: Option<FormattedValue<f64>>,
72 market_cap: Option<FormattedValue<f64>>,
73 market_weight: Option<FormattedValue<f64>>,
74 #[serde(rename = "regMarketChangePercent")]
75 day_change_percent: Option<FormattedValue<f64>>,
76 ytd_return: Option<FormattedValue<f64>>,
77 rating: Option<String>,
78 target_price: Option<FormattedValue<f64>>,
79}
80
81#[derive(Debug, Clone, Deserialize)]
82#[serde(rename_all = "camelCase")]
83struct RawPerformingCompany {
84 symbol: String,
85 name: Option<String>,
86 last_price: Option<FormattedValue<f64>>,
87 ytd_return: Option<FormattedValue<f64>>,
88 target_price: Option<FormattedValue<f64>>,
89}
90
91#[derive(Debug, Clone, Deserialize)]
92#[serde(rename_all = "camelCase")]
93struct RawGrowthCompany {
94 symbol: String,
95 name: Option<String>,
96 last_price: Option<FormattedValue<f64>>,
97 ytd_return: Option<FormattedValue<f64>>,
98 growth_estimate: Option<FormattedValue<f64>>,
99}
100
101#[derive(Debug, Clone, Deserialize)]
102#[serde(rename_all = "camelCase")]
103struct RawResearchReport {
104 id: Option<String>,
105 #[serde(rename = "reportTitle")]
106 title: Option<String>,
107 provider: Option<String>,
108 report_date: Option<String>,
109 report_type: Option<String>,
110 investment_rating: Option<String>,
111 target_price: Option<f64>,
112 target_price_status: Option<String>,
113}
114
115#[derive(Debug, Clone, Serialize, Deserialize)]
121#[serde(rename_all = "camelCase")]
122#[non_exhaustive]
123pub struct IndustryData {
124 pub name: String,
126 pub key: String,
128 #[serde(skip_serializing_if = "Option::is_none")]
130 pub symbol: Option<String>,
131 #[serde(skip_serializing_if = "Option::is_none")]
133 pub sector_name: Option<String>,
134 #[serde(skip_serializing_if = "Option::is_none")]
136 pub sector_key: Option<String>,
137 #[serde(skip_serializing_if = "Option::is_none")]
139 pub overview: Option<IndustryOverview>,
140 #[serde(skip_serializing_if = "Option::is_none")]
142 pub performance: Option<IndustryPerformance>,
143 #[serde(skip_serializing_if = "Option::is_none")]
145 pub benchmark: Option<BenchmarkPerformance>,
146 #[serde(default)]
148 pub top_companies: Vec<IndustryCompany>,
149 #[serde(default)]
151 pub top_performing_companies: Vec<PerformingCompany>,
152 #[serde(default)]
154 pub top_growth_companies: Vec<GrowthCompany>,
155 #[serde(default)]
157 pub research_reports: Vec<ResearchReport>,
158}
159
160#[derive(Debug, Clone, Serialize, Deserialize)]
162#[cfg_attr(feature = "dataframe", derive(crate::ToDataFrame))]
163#[serde(rename_all = "camelCase")]
164#[non_exhaustive]
165pub struct IndustryOverview {
166 #[serde(skip_serializing_if = "Option::is_none")]
168 pub description: Option<String>,
169 #[serde(skip_serializing_if = "Option::is_none")]
171 pub companies_count: Option<i64>,
172 #[serde(skip_serializing_if = "Option::is_none")]
174 pub market_cap: Option<f64>,
175 #[serde(skip_serializing_if = "Option::is_none")]
177 pub market_weight: Option<f64>,
178 #[serde(skip_serializing_if = "Option::is_none")]
180 pub employee_count: Option<i64>,
181}
182
183#[derive(Debug, Clone, Serialize, Deserialize)]
185#[cfg_attr(feature = "dataframe", derive(crate::ToDataFrame))]
186#[serde(rename_all = "camelCase")]
187#[non_exhaustive]
188pub struct IndustryPerformance {
189 #[serde(skip_serializing_if = "Option::is_none")]
191 pub day_change_percent: Option<f64>,
192 #[serde(skip_serializing_if = "Option::is_none")]
194 pub ytd_change_percent: Option<f64>,
195 #[serde(skip_serializing_if = "Option::is_none")]
197 pub one_year_change_percent: Option<f64>,
198 #[serde(skip_serializing_if = "Option::is_none")]
200 pub three_year_change_percent: Option<f64>,
201 #[serde(skip_serializing_if = "Option::is_none")]
203 pub five_year_change_percent: Option<f64>,
204}
205
206#[derive(Debug, Clone, Serialize, Deserialize)]
208#[cfg_attr(feature = "dataframe", derive(crate::ToDataFrame))]
209#[serde(rename_all = "camelCase")]
210#[non_exhaustive]
211pub struct BenchmarkPerformance {
212 #[serde(skip_serializing_if = "Option::is_none")]
214 pub name: Option<String>,
215 #[serde(skip_serializing_if = "Option::is_none")]
217 pub day_change_percent: Option<f64>,
218 #[serde(skip_serializing_if = "Option::is_none")]
220 pub ytd_change_percent: Option<f64>,
221 #[serde(skip_serializing_if = "Option::is_none")]
223 pub one_year_change_percent: Option<f64>,
224 #[serde(skip_serializing_if = "Option::is_none")]
226 pub three_year_change_percent: Option<f64>,
227 #[serde(skip_serializing_if = "Option::is_none")]
229 pub five_year_change_percent: Option<f64>,
230}
231
232#[derive(Debug, Clone, Serialize, Deserialize)]
234#[cfg_attr(feature = "dataframe", derive(crate::ToDataFrame))]
235#[serde(rename_all = "camelCase")]
236#[non_exhaustive]
237pub struct IndustryCompany {
238 pub symbol: String,
240 #[serde(skip_serializing_if = "Option::is_none")]
242 pub name: Option<String>,
243 #[serde(skip_serializing_if = "Option::is_none")]
245 pub last_price: Option<f64>,
246 #[serde(skip_serializing_if = "Option::is_none")]
248 pub market_cap: Option<f64>,
249 #[serde(skip_serializing_if = "Option::is_none")]
251 pub market_weight: Option<f64>,
252 #[serde(skip_serializing_if = "Option::is_none")]
254 pub day_change_percent: Option<f64>,
255 #[serde(skip_serializing_if = "Option::is_none")]
257 pub ytd_return: Option<f64>,
258 #[serde(skip_serializing_if = "Option::is_none")]
260 pub rating: Option<String>,
261 #[serde(skip_serializing_if = "Option::is_none")]
263 pub target_price: Option<f64>,
264}
265
266#[derive(Debug, Clone, Serialize, Deserialize)]
268#[cfg_attr(feature = "dataframe", derive(crate::ToDataFrame))]
269#[serde(rename_all = "camelCase")]
270#[non_exhaustive]
271pub struct PerformingCompany {
272 pub symbol: String,
274 #[serde(skip_serializing_if = "Option::is_none")]
276 pub name: Option<String>,
277 #[serde(skip_serializing_if = "Option::is_none")]
279 pub last_price: Option<f64>,
280 #[serde(skip_serializing_if = "Option::is_none")]
282 pub ytd_return: Option<f64>,
283 #[serde(skip_serializing_if = "Option::is_none")]
285 pub target_price: Option<f64>,
286}
287
288#[derive(Debug, Clone, Serialize, Deserialize)]
290#[cfg_attr(feature = "dataframe", derive(crate::ToDataFrame))]
291#[serde(rename_all = "camelCase")]
292#[non_exhaustive]
293pub struct GrowthCompany {
294 pub symbol: String,
296 #[serde(skip_serializing_if = "Option::is_none")]
298 pub name: Option<String>,
299 #[serde(skip_serializing_if = "Option::is_none")]
301 pub last_price: Option<f64>,
302 #[serde(skip_serializing_if = "Option::is_none")]
304 pub ytd_return: Option<f64>,
305 #[serde(skip_serializing_if = "Option::is_none")]
307 pub growth_estimate: Option<f64>,
308}
309
310#[derive(Debug, Clone, Serialize, Deserialize)]
312#[cfg_attr(feature = "dataframe", derive(crate::ToDataFrame))]
313#[serde(rename_all = "camelCase")]
314#[non_exhaustive]
315pub struct ResearchReport {
316 #[serde(skip_serializing_if = "Option::is_none")]
318 pub id: Option<String>,
319 #[serde(skip_serializing_if = "Option::is_none")]
321 pub title: Option<String>,
322 #[serde(skip_serializing_if = "Option::is_none")]
324 pub provider: Option<String>,
325 #[serde(skip_serializing_if = "Option::is_none")]
327 pub report_date: Option<String>,
328 #[serde(skip_serializing_if = "Option::is_none")]
330 pub report_type: Option<String>,
331 #[serde(skip_serializing_if = "Option::is_none")]
333 pub investment_rating: Option<String>,
334 #[serde(skip_serializing_if = "Option::is_none")]
336 pub target_price: Option<f64>,
337 #[serde(skip_serializing_if = "Option::is_none")]
339 pub target_price_status: Option<String>,
340}
341
342impl IndustryData {
347 pub(crate) fn from_response(json: serde_json::Value) -> Result<Self, String> {
349 let raw: RawIndustryResponse = serde_json::from_value(json).map_err(|e| e.to_string())?;
350
351 let data = raw.data;
352
353 Ok(IndustryData {
354 name: data.name,
355 key: data.key,
356 symbol: data.symbol,
357 sector_name: data.sector_name,
358 sector_key: data.sector_key,
359 overview: data.overview.map(|o| IndustryOverview {
360 description: o.description,
361 companies_count: o.companies_count,
362 market_cap: o.market_cap.and_then(|v| v.raw),
363 market_weight: o.market_weight.and_then(|v| v.raw),
364 employee_count: o.employee_count.and_then(|v| v.raw),
365 }),
366 performance: data.performance.map(|p| IndustryPerformance {
367 day_change_percent: p.reg_market_change_percent.and_then(|v| v.raw),
368 ytd_change_percent: p.ytd_change_percent.and_then(|v| v.raw),
369 one_year_change_percent: p.one_year_change_percent.and_then(|v| v.raw),
370 three_year_change_percent: p.three_year_change_percent.and_then(|v| v.raw),
371 five_year_change_percent: p.five_year_change_percent.and_then(|v| v.raw),
372 }),
373 benchmark: data
374 .performance_overview_benchmark
375 .map(|b| BenchmarkPerformance {
376 name: b.name,
377 day_change_percent: b.reg_market_change_percent.and_then(|v| v.raw),
378 ytd_change_percent: b.ytd_change_percent.and_then(|v| v.raw),
379 one_year_change_percent: b.one_year_change_percent.and_then(|v| v.raw),
380 three_year_change_percent: b.three_year_change_percent.and_then(|v| v.raw),
381 five_year_change_percent: b.five_year_change_percent.and_then(|v| v.raw),
382 }),
383 top_companies: data
384 .top_companies
385 .into_iter()
386 .map(|c| IndustryCompany {
387 symbol: c.symbol,
388 name: c.name,
389 last_price: c.last_price.and_then(|v| v.raw),
390 market_cap: c.market_cap.and_then(|v| v.raw),
391 market_weight: c.market_weight.and_then(|v| v.raw),
392 day_change_percent: c.day_change_percent.and_then(|v| v.raw),
393 ytd_return: c.ytd_return.and_then(|v| v.raw),
394 rating: c.rating,
395 target_price: c.target_price.and_then(|v| v.raw),
396 })
397 .collect(),
398 top_performing_companies: data
399 .top_performing_companies
400 .into_iter()
401 .map(|c| PerformingCompany {
402 symbol: c.symbol,
403 name: c.name,
404 last_price: c.last_price.and_then(|v| v.raw),
405 ytd_return: c.ytd_return.and_then(|v| v.raw),
406 target_price: c.target_price.and_then(|v| v.raw),
407 })
408 .collect(),
409 top_growth_companies: data
410 .top_growth_companies
411 .into_iter()
412 .map(|c| GrowthCompany {
413 symbol: c.symbol,
414 name: c.name,
415 last_price: c.last_price.and_then(|v| v.raw),
416 ytd_return: c.ytd_return.and_then(|v| v.raw),
417 growth_estimate: c.growth_estimate.and_then(|v| v.raw),
418 })
419 .collect(),
420 research_reports: data
421 .research_reports
422 .into_iter()
423 .map(|r| ResearchReport {
424 id: r.id,
425 title: r.title,
426 provider: r.provider,
427 report_date: r.report_date,
428 report_type: r.report_type,
429 investment_rating: r.investment_rating,
430 target_price: r.target_price,
431 target_price_status: r.target_price_status,
432 })
433 .collect(),
434 })
435 }
436}