Skip to main content

pomelo_data/
industry.rs

1//! Build the `symbol -> sector` map the yuzu-core neutralization ops need,
2//! from the `tracked/*.csv.gz` snapshot (`symbol,sector,market_cap`).
3
4use std::collections::HashMap;
5
6/// Parse a `symbol,sector,...` CSV into `symbol -> sector`. A leading header row
7/// (`symbol,...`) is skipped; rows with an empty sector are dropped.
8pub fn parse_industry_csv(csv: &str) -> HashMap<String, String> {
9    let mut out = HashMap::new();
10    for line in csv.lines() {
11        let line = line.trim();
12        if line.is_empty() {
13            continue;
14        }
15        let mut fields = line.split(',');
16        let (Some(sym), Some(sector)) = (fields.next(), fields.next()) else {
17            continue;
18        };
19        let (sym, sector) = (sym.trim(), sector.trim());
20        if sym.is_empty() || sector.is_empty() || sym == "symbol" {
21            continue;
22        }
23        out.insert(sym.to_string(), sector.to_string());
24    }
25    out
26}
27
28#[cfg(test)]
29mod tests {
30    use super::*;
31
32    #[test]
33    fn parses_symbol_sector_skipping_header_and_blanks() {
34        let csv = "symbol,sector,market_cap\nNVDA,Technology,5103000000000\nXOM,Energy,470000000000\nUNKNOWN,,1000\n";
35        let m = parse_industry_csv(csv);
36        assert_eq!(m.get("NVDA").map(String::as_str), Some("Technology"));
37        assert_eq!(m.get("XOM").map(String::as_str), Some("Energy"));
38        assert!(!m.contains_key("UNKNOWN")); // empty sector dropped
39        assert!(!m.contains_key("symbol")); // header skipped
40        assert_eq!(m.len(), 2);
41    }
42}