finance_query_core/models/
sectors.rs1use serde::{Deserialize, Serialize};
2use std::str::FromStr;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
5#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
6pub enum Sector {
7 #[serde(rename = "Basic Materials")]
8 BasicMaterials,
9 #[serde(rename = "Communication Services")]
10 Communication,
11 #[serde(rename = "Consumer Cyclical")]
12 ConsumerCyclical,
13 #[serde(rename = "Consumer Defensive")]
14 ConsumerDefensive,
15 Energy,
16 #[serde(rename = "Financial Services")]
17 FinancialServices,
18 Healthcare,
19 Industrials,
20 #[serde(rename = "Real Estate")]
21 RealEstate,
22 Technology,
23 Utilities,
24}
25
26impl Sector {
27 pub fn as_str(&self) -> &'static str {
28 match self {
29 Sector::BasicMaterials => "Basic Materials",
30 Sector::Communication => "Communication Services",
31 Sector::ConsumerCyclical => "Consumer Cyclical",
32 Sector::ConsumerDefensive => "Consumer Defensive",
33 Sector::Energy => "Energy",
34 Sector::FinancialServices => "Financial Services",
35 Sector::Healthcare => "Healthcare",
36 Sector::Industrials => "Industrials",
37 Sector::RealEstate => "Real Estate",
38 Sector::Technology => "Technology",
39 Sector::Utilities => "Utilities",
40 }
41 }
42
43 #[allow(dead_code)]
44 pub fn url_path(&self) -> &'static str {
45 match self {
46 Sector::BasicMaterials => "basic-materials",
47 Sector::Communication => "communication-services",
48 Sector::ConsumerCyclical => "consumer-cyclical",
49 Sector::ConsumerDefensive => "consumer-defensive",
50 Sector::Energy => "energy",
51 Sector::FinancialServices => "financial-services",
52 Sector::Healthcare => "healthcare",
53 Sector::Industrials => "industrials",
54 Sector::RealEstate => "real-estate",
55 Sector::Technology => "technology",
56 Sector::Utilities => "utilities",
57 }
58 }
59
60
61 #[allow(dead_code)]
62 pub fn all() -> Vec<Sector> {
63 vec![
64 Sector::Technology,
65 Sector::Healthcare,
66 Sector::FinancialServices,
67 Sector::ConsumerCyclical,
68 Sector::Industrials,
69 Sector::ConsumerDefensive,
70 Sector::Energy,
71 Sector::RealEstate,
72 Sector::Utilities,
73 Sector::BasicMaterials,
74 Sector::Communication,
75 ]
76 }
77}
78
79impl FromStr for Sector {
80 type Err = String;
81
82 fn from_str(s: &str) -> Result<Self, Self::Err> {
83 match s {
84 "Basic Materials" => Ok(Sector::BasicMaterials),
85 "Communication Services" => Ok(Sector::Communication),
86 "Consumer Cyclical" => Ok(Sector::ConsumerCyclical),
87 "Consumer Defensive" => Ok(Sector::ConsumerDefensive),
88 "Energy" => Ok(Sector::Energy),
89 "Financial Services" => Ok(Sector::FinancialServices),
90 "Healthcare" => Ok(Sector::Healthcare),
91 "Industrials" => Ok(Sector::Industrials),
92 "Real Estate" => Ok(Sector::RealEstate),
93 "Technology" => Ok(Sector::Technology),
94 "Utilities" => Ok(Sector::Utilities),
95 _ => Err(format!("Invalid sector: {}", s)),
96 }
97 }
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize)]
101pub struct MarketSector {
102 pub sector: String,
103 #[serde(rename = "dayReturn")]
104 pub day_return: String,
105 #[serde(rename = "ytdReturn")]
106 pub ytd_return: String,
107 #[serde(rename = "yearReturn")]
108 pub year_return: String,
109 #[serde(rename = "threeYearReturn")]
110 pub three_year_return: String,
111 #[serde(rename = "fiveYearReturn")]
112 pub five_year_return: String,
113}
114
115#[derive(Debug, Clone, Serialize, Deserialize)]
116pub struct MarketSectorDetails {
117 pub sector: String,
118 #[serde(rename = "dayReturn")]
119 pub day_return: String,
120 #[serde(rename = "ytdReturn")]
121 pub ytd_return: String,
122 #[serde(rename = "yearReturn")]
123 pub year_return: String,
124 #[serde(rename = "threeYearReturn")]
125 pub three_year_return: String,
126 #[serde(rename = "fiveYearReturn")]
127 pub five_year_return: String,
128 #[serde(rename = "marketCap")]
129 pub market_cap: String,
130 #[serde(rename = "marketWeight")]
131 pub market_weight: String,
132 pub industries: i32,
133 pub companies: i32,
134 #[serde(rename = "topIndustries")]
135 pub top_industries: Vec<String>,
136 #[serde(rename = "topCompanies")]
137 pub top_companies: Vec<String>,
138}
139
140#[cfg(test)]
141mod tests {
142 use super::*;
143 use proptest::prelude::*;
144
145 proptest! {
148 #![proptest_config(ProptestConfig::with_cases(100))]
149
150 #[test]
151 fn market_sector_roundtrip(
152 sector in "[A-Za-z ]{1,30}",
153 day_return in "-?[0-9]{1,3}\\.[0-9]{2}%",
154 ytd_return in "-?[0-9]{1,3}\\.[0-9]{2}%",
155 year_return in "-?[0-9]{1,3}\\.[0-9]{2}%",
156 three_year_return in "-?[0-9]{1,3}\\.[0-9]{2}%",
157 five_year_return in "-?[0-9]{1,3}\\.[0-9]{2}%",
158 ) {
159 let ms = MarketSector {
160 sector: sector.clone(),
161 day_return: day_return.clone(),
162 ytd_return: ytd_return.clone(),
163 year_return: year_return.clone(),
164 three_year_return: three_year_return.clone(),
165 five_year_return: five_year_return.clone(),
166 };
167
168 let json = serde_json::to_string(&ms).unwrap();
169 let parsed: MarketSector = serde_json::from_str(&json).unwrap();
170
171 prop_assert_eq!(ms.sector, parsed.sector);
172 prop_assert_eq!(ms.day_return, parsed.day_return);
173 prop_assert_eq!(ms.ytd_return, parsed.ytd_return);
174 prop_assert_eq!(ms.year_return, parsed.year_return);
175 }
176
177 #[test]
178 fn sector_roundtrip(sector in prop_oneof![
179 Just(Sector::BasicMaterials),
180 Just(Sector::Communication),
181 Just(Sector::ConsumerCyclical),
182 Just(Sector::ConsumerDefensive),
183 Just(Sector::Energy),
184 Just(Sector::FinancialServices),
185 Just(Sector::Healthcare),
186 Just(Sector::Industrials),
187 Just(Sector::RealEstate),
188 Just(Sector::Technology),
189 Just(Sector::Utilities),
190 ]) {
191 let json = serde_json::to_string(§or).unwrap();
192 let parsed: Sector = serde_json::from_str(&json).unwrap();
193
194 prop_assert_eq!(sector.as_str(), parsed.as_str());
195 }
196 }
197}