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 Industry {
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 Industry {
347 pub(crate) fn from_response(json: &serde_json::Value) -> Result<Self, String> {
349 let raw: RawIndustryResponse =
350 serde_json::from_value(json.clone()).map_err(|e| e.to_string())?;
351
352 let data = raw.data;
353
354 Ok(Industry {
355 name: data.name,
356 key: data.key,
357 symbol: data.symbol,
358 sector_name: data.sector_name,
359 sector_key: data.sector_key,
360 overview: data.overview.map(|o| IndustryOverview {
361 description: o.description,
362 companies_count: o.companies_count,
363 market_cap: o.market_cap.and_then(|v| v.raw),
364 market_weight: o.market_weight.and_then(|v| v.raw),
365 employee_count: o.employee_count.and_then(|v| v.raw),
366 }),
367 performance: data.performance.map(|p| IndustryPerformance {
368 day_change_percent: p.reg_market_change_percent.and_then(|v| v.raw),
369 ytd_change_percent: p.ytd_change_percent.and_then(|v| v.raw),
370 one_year_change_percent: p.one_year_change_percent.and_then(|v| v.raw),
371 three_year_change_percent: p.three_year_change_percent.and_then(|v| v.raw),
372 five_year_change_percent: p.five_year_change_percent.and_then(|v| v.raw),
373 }),
374 benchmark: data
375 .performance_overview_benchmark
376 .map(|b| BenchmarkPerformance {
377 name: b.name,
378 day_change_percent: b.reg_market_change_percent.and_then(|v| v.raw),
379 ytd_change_percent: b.ytd_change_percent.and_then(|v| v.raw),
380 one_year_change_percent: b.one_year_change_percent.and_then(|v| v.raw),
381 three_year_change_percent: b.three_year_change_percent.and_then(|v| v.raw),
382 five_year_change_percent: b.five_year_change_percent.and_then(|v| v.raw),
383 }),
384 top_companies: data
385 .top_companies
386 .into_iter()
387 .map(|c| IndustryCompany {
388 symbol: c.symbol,
389 name: c.name,
390 last_price: c.last_price.and_then(|v| v.raw),
391 market_cap: c.market_cap.and_then(|v| v.raw),
392 market_weight: c.market_weight.and_then(|v| v.raw),
393 day_change_percent: c.day_change_percent.and_then(|v| v.raw),
394 ytd_return: c.ytd_return.and_then(|v| v.raw),
395 rating: c.rating,
396 target_price: c.target_price.and_then(|v| v.raw),
397 })
398 .collect(),
399 top_performing_companies: data
400 .top_performing_companies
401 .into_iter()
402 .map(|c| PerformingCompany {
403 symbol: c.symbol,
404 name: c.name,
405 last_price: c.last_price.and_then(|v| v.raw),
406 ytd_return: c.ytd_return.and_then(|v| v.raw),
407 target_price: c.target_price.and_then(|v| v.raw),
408 })
409 .collect(),
410 top_growth_companies: data
411 .top_growth_companies
412 .into_iter()
413 .map(|c| GrowthCompany {
414 symbol: c.symbol,
415 name: c.name,
416 last_price: c.last_price.and_then(|v| v.raw),
417 ytd_return: c.ytd_return.and_then(|v| v.raw),
418 growth_estimate: c.growth_estimate.and_then(|v| v.raw),
419 })
420 .collect(),
421 research_reports: data
422 .research_reports
423 .into_iter()
424 .map(|r| ResearchReport {
425 id: r.id,
426 title: r.title,
427 provider: r.provider,
428 report_date: r.report_date,
429 report_type: r.report_type,
430 investment_rating: r.investment_rating,
431 target_price: r.target_price,
432 target_price_status: r.target_price_status,
433 })
434 .collect(),
435 })
436 }
437}