Skip to main content

finance_query_core/models/
movers.rs

1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)]
4#[serde(rename_all = "snake_case")]
5pub enum MoverCount {
6    #[serde(rename = "25")]
7    TwentyFive,
8    #[serde(rename = "50")]
9    #[default]
10    Fifty,
11    #[serde(rename = "100")]
12    Hundred,
13}
14
15impl MoverCount {
16    pub fn as_str(&self) -> &'static str {
17        match self {
18            MoverCount::TwentyFive => "25",
19            MoverCount::Fifty => "50",
20            MoverCount::Hundred => "100",
21        }
22    }
23
24    pub fn parse(s: &str) -> Option<Self> {
25        match s {
26            "25" => Some(MoverCount::TwentyFive),
27            "50" => Some(MoverCount::Fifty),
28            "100" => Some(MoverCount::Hundred),
29            _ => None,
30        }
31    }
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
35#[serde(rename_all = "snake_case")]
36pub struct MarketMover {
37    pub symbol: String,
38    pub name: String,
39    pub price: String,
40    pub change: String,
41    #[serde(rename = "percentChange")]
42    pub percent_change: String,
43}
44
45#[cfg(test)]
46mod tests {
47    use super::*;
48    use proptest::prelude::*;
49
50    // **Feature: crate-extraction, Property 1: Model Serialization Round-Trip**
51    // **Validates: Requirements 2.2**
52    proptest! {
53        #![proptest_config(ProptestConfig::with_cases(100))]
54
55        #[test]
56        fn market_mover_roundtrip(
57            symbol in "[A-Z]{1,5}",
58            name in "[A-Za-z ]{1,50}",
59            price in "[0-9]{1,4}\\.[0-9]{2}",
60            change in "-?[0-9]{1,3}\\.[0-9]{2}",
61            percent_change in "-?[0-9]{1,3}\\.[0-9]{2}%",
62        ) {
63            let mover = MarketMover {
64                symbol: symbol.clone(),
65                name: name.clone(),
66                price: price.clone(),
67                change: change.clone(),
68                percent_change: percent_change.clone(),
69            };
70
71            let json = serde_json::to_string(&mover).unwrap();
72            let parsed: MarketMover = serde_json::from_str(&json).unwrap();
73
74            prop_assert_eq!(mover.symbol, parsed.symbol);
75            prop_assert_eq!(mover.name, parsed.name);
76            prop_assert_eq!(mover.price, parsed.price);
77            prop_assert_eq!(mover.change, parsed.change);
78            prop_assert_eq!(mover.percent_change, parsed.percent_change);
79        }
80
81        #[test]
82        fn mover_count_roundtrip(count in prop_oneof![
83            Just(MoverCount::TwentyFive),
84            Just(MoverCount::Fifty),
85            Just(MoverCount::Hundred),
86        ]) {
87            let json = serde_json::to_string(&count).unwrap();
88            let parsed: MoverCount = serde_json::from_str(&json).unwrap();
89
90            prop_assert_eq!(count.as_str(), parsed.as_str());
91        }
92    }
93}