Skip to main content

finance_query/constants/
sectors.rs

1use serde::{Deserialize, Serialize};
2
3/// Market sector types available on Yahoo Finance
4///
5/// The `alias`es mirror the shorthands [`FromStr`](std::str::FromStr) accepts, so
6/// deserializing (axum path extraction, JSON) takes the same spellings parsing does.
7#[non_exhaustive]
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
9#[serde(rename_all = "kebab-case")]
10pub enum Sector {
11    /// Technology sector (software, semiconductors, hardware)
12    #[serde(alias = "tech")]
13    Technology,
14    /// Financial Services sector (banks, insurance, asset management)
15    #[serde(alias = "financials", alias = "financial")]
16    FinancialServices,
17    /// Consumer Cyclical sector (retail, automotive, leisure)
18    ConsumerCyclical,
19    /// Communication Services sector (telecom, media, entertainment)
20    #[serde(alias = "communication")]
21    CommunicationServices,
22    /// Healthcare sector (pharma, biotech, medical devices)
23    #[serde(alias = "health")]
24    Healthcare,
25    /// Industrials sector (aerospace, machinery, construction)
26    #[serde(alias = "industrial")]
27    Industrials,
28    /// Consumer Defensive sector (food, beverages, household products)
29    ConsumerDefensive,
30    /// Energy sector (oil, gas, renewable energy)
31    Energy,
32    /// Basic Materials sector (chemicals, metals, mining)
33    #[serde(alias = "materials")]
34    BasicMaterials,
35    /// Real Estate sector (REITs, property management)
36    #[serde(alias = "realestate")]
37    RealEstate,
38    /// Utilities sector (electric, gas, water utilities)
39    #[serde(alias = "utility")]
40    Utilities,
41}
42
43impl Sector {
44    /// Convert to Yahoo Finance API path segment (lowercase with hyphens)
45    pub fn as_api_path(&self) -> &'static str {
46        match self {
47            Sector::Technology => "technology",
48            Sector::FinancialServices => "financial-services",
49            Sector::ConsumerCyclical => "consumer-cyclical",
50            Sector::CommunicationServices => "communication-services",
51            Sector::Healthcare => "healthcare",
52            Sector::Industrials => "industrials",
53            Sector::ConsumerDefensive => "consumer-defensive",
54            Sector::Energy => "energy",
55            Sector::BasicMaterials => "basic-materials",
56            Sector::RealEstate => "real-estate",
57            Sector::Utilities => "utilities",
58        }
59    }
60
61    /// Get human-readable display name
62    pub fn display_name(&self) -> &'static str {
63        match self {
64            Sector::Technology => "Technology",
65            Sector::FinancialServices => "Financial Services",
66            Sector::ConsumerCyclical => "Consumer Cyclical",
67            Sector::CommunicationServices => "Communication Services",
68            Sector::Healthcare => "Healthcare",
69            Sector::Industrials => "Industrials",
70            Sector::ConsumerDefensive => "Consumer Defensive",
71            Sector::Energy => "Energy",
72            Sector::BasicMaterials => "Basic Materials",
73            Sector::RealEstate => "Real Estate",
74            Sector::Utilities => "Utilities",
75        }
76    }
77
78    /// SPDR Select Sector ETF tracking this GICS sector, used to derive
79    /// sector performance history from ETF price action when no provider
80    /// serves it directly.
81    pub fn spdr_etf(&self) -> &'static str {
82        match self {
83            Sector::Technology => "XLK",
84            Sector::FinancialServices => "XLF",
85            Sector::ConsumerCyclical => "XLY",
86            Sector::CommunicationServices => "XLC",
87            Sector::Healthcare => "XLV",
88            Sector::Industrials => "XLI",
89            Sector::ConsumerDefensive => "XLP",
90            Sector::Energy => "XLE",
91            Sector::BasicMaterials => "XLB",
92            Sector::RealEstate => "XLRE",
93            Sector::Utilities => "XLU",
94        }
95    }
96
97    /// List all valid sector types for error messages
98    pub fn valid_types() -> &'static str {
99        "technology, financial-services, consumer-cyclical, communication-services, \
100         healthcare, industrials, consumer-defensive, energy, basic-materials, \
101         real-estate, utilities"
102    }
103
104    /// Get all sector types as an array
105    pub fn all() -> &'static [Sector] {
106        &[
107            Sector::Technology,
108            Sector::FinancialServices,
109            Sector::ConsumerCyclical,
110            Sector::CommunicationServices,
111            Sector::Healthcare,
112            Sector::Industrials,
113            Sector::ConsumerDefensive,
114            Sector::Energy,
115            Sector::BasicMaterials,
116            Sector::RealEstate,
117            Sector::Utilities,
118        ]
119    }
120}
121
122impl std::str::FromStr for Sector {
123    type Err = ();
124
125    fn from_str(s: &str) -> Result<Self, Self::Err> {
126        match s.to_lowercase().replace('_', "-").as_str() {
127            "technology" | "tech" => Ok(Sector::Technology),
128            "financial-services" | "financials" | "financial" => Ok(Sector::FinancialServices),
129            "consumer-cyclical" => Ok(Sector::ConsumerCyclical),
130            "communication-services" | "communication" => Ok(Sector::CommunicationServices),
131            "healthcare" | "health" => Ok(Sector::Healthcare),
132            "industrials" | "industrial" => Ok(Sector::Industrials),
133            "consumer-defensive" => Ok(Sector::ConsumerDefensive),
134            "energy" => Ok(Sector::Energy),
135            "basic-materials" | "materials" => Ok(Sector::BasicMaterials),
136            "real-estate" | "realestate" => Ok(Sector::RealEstate),
137            "utilities" | "utility" => Ok(Sector::Utilities),
138            _ => Err(()),
139        }
140    }
141}
142
143impl std::fmt::Display for Sector {
144    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
145        write!(f, "{}", self.display_name())
146    }
147}
148
149impl From<Sector> for String {
150    /// Returns the display name used by Yahoo Finance screener (e.g. `"Technology"`).
151    fn from(v: Sector) -> Self {
152        v.display_name().to_string()
153    }
154}