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 #[allow(dead_code)]
61 pub fn all() -> Vec<Sector> {
62 vec![
63 Sector::Technology,
64 Sector::Healthcare,
65 Sector::FinancialServices,
66 Sector::ConsumerCyclical,
67 Sector::Industrials,
68 Sector::ConsumerDefensive,
69 Sector::Energy,
70 Sector::RealEstate,
71 Sector::Utilities,
72 Sector::BasicMaterials,
73 Sector::Communication,
74 ]
75 }
76}
77
78impl FromStr for Sector {
79 type Err = String;
80
81 fn from_str(s: &str) -> Result<Self, Self::Err> {
82 match s {
83 "Basic Materials" => Ok(Sector::BasicMaterials),
84 "Communication Services" => Ok(Sector::Communication),
85 "Consumer Cyclical" => Ok(Sector::ConsumerCyclical),
86 "Consumer Defensive" => Ok(Sector::ConsumerDefensive),
87 "Energy" => Ok(Sector::Energy),
88 "Financial Services" => Ok(Sector::FinancialServices),
89 "Healthcare" => Ok(Sector::Healthcare),
90 "Industrials" => Ok(Sector::Industrials),
91 "Real Estate" => Ok(Sector::RealEstate),
92 "Technology" => Ok(Sector::Technology),
93 "Utilities" => Ok(Sector::Utilities),
94 _ => Err(format!("Invalid sector: {}", s)),
95 }
96 }
97}
98
99#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct MarketSector {
101 pub sector: String,
102 #[serde(rename = "dayReturn")]
103 pub day_return: String,
104 #[serde(rename = "ytdReturn")]
105 pub ytd_return: String,
106 #[serde(rename = "yearReturn")]
107 pub year_return: String,
108 #[serde(rename = "threeYearReturn")]
109 pub three_year_return: String,
110 #[serde(rename = "fiveYearReturn")]
111 pub five_year_return: String,
112}
113
114#[derive(Debug, Clone, Serialize, Deserialize)]
115pub struct MarketSectorDetails {
116 pub sector: String,
117 #[serde(rename = "dayReturn")]
118 pub day_return: String,
119 #[serde(rename = "ytdReturn")]
120 pub ytd_return: String,
121 #[serde(rename = "yearReturn")]
122 pub year_return: String,
123 #[serde(rename = "threeYearReturn")]
124 pub three_year_return: String,
125 #[serde(rename = "fiveYearReturn")]
126 pub five_year_return: String,
127 #[serde(rename = "marketCap")]
128 pub market_cap: String,
129 #[serde(rename = "marketWeight")]
130 pub market_weight: String,
131 pub industries: i32,
132 pub companies: i32,
133 #[serde(rename = "topIndustries")]
134 pub top_industries: Vec<String>,
135 #[serde(rename = "topCompanies")]
136 pub top_companies: Vec<String>,
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142 use proptest::prelude::*;
143
144 proptest! {
147 #![proptest_config(ProptestConfig::with_cases(100))]
148
149 #[test]
150 fn market_sector_roundtrip(
151 sector in "[A-Za-z ]{1,30}",
152 day_return in "-?[0-9]{1,3}\\.[0-9]{2}%",
153 ytd_return in "-?[0-9]{1,3}\\.[0-9]{2}%",
154 year_return in "-?[0-9]{1,3}\\.[0-9]{2}%",
155 three_year_return in "-?[0-9]{1,3}\\.[0-9]{2}%",
156 five_year_return in "-?[0-9]{1,3}\\.[0-9]{2}%",
157 ) {
158 let ms = MarketSector {
159 sector: sector.clone(),
160 day_return: day_return.clone(),
161 ytd_return: ytd_return.clone(),
162 year_return: year_return.clone(),
163 three_year_return: three_year_return.clone(),
164 five_year_return: five_year_return.clone(),
165 };
166
167 let json = serde_json::to_string(&ms).unwrap();
168 let parsed: MarketSector = serde_json::from_str(&json).unwrap();
169
170 prop_assert_eq!(ms.sector, parsed.sector);
171 prop_assert_eq!(ms.day_return, parsed.day_return);
172 prop_assert_eq!(ms.ytd_return, parsed.ytd_return);
173 prop_assert_eq!(ms.year_return, parsed.year_return);
174 }
175
176 #[test]
177 fn sector_roundtrip(sector in prop_oneof![
178 Just(Sector::BasicMaterials),
179 Just(Sector::Communication),
180 Just(Sector::ConsumerCyclical),
181 Just(Sector::ConsumerDefensive),
182 Just(Sector::Energy),
183 Just(Sector::FinancialServices),
184 Just(Sector::Healthcare),
185 Just(Sector::Industrials),
186 Just(Sector::RealEstate),
187 Just(Sector::Technology),
188 Just(Sector::Utilities),
189 ]) {
190 let json = serde_json::to_string(§or).unwrap();
191 let parsed: Sector = serde_json::from_str(&json).unwrap();
192
193 prop_assert_eq!(sector.as_str(), parsed.as_str());
194 }
195 }
196}