Skip to main content

finance_query_core/models/
financials.rs

1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
5#[serde(rename_all = "snake_case")]
6pub enum StatementType {
7    #[serde(rename = "income")]
8    IncomeStatement,
9    #[serde(rename = "balance")]
10    BalanceSheet,
11    #[serde(rename = "cashflow")]
12    CashFlow,
13}
14
15impl StatementType {
16    #[allow(dead_code)]
17    pub fn as_str(&self) -> &'static str {
18        match self {
19            StatementType::IncomeStatement => "income",
20            StatementType::BalanceSheet => "balance",
21            StatementType::CashFlow => "cashflow",
22        }
23    }
24}
25
26#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
27#[serde(rename_all = "snake_case")]
28pub enum Frequency {
29    #[serde(rename = "annual")]
30    Annual,
31    #[serde(rename = "quarterly")]
32    Quarterly,
33}
34
35impl Frequency {
36    pub fn as_str(&self) -> &'static str {
37        match self {
38            Frequency::Annual => "annual",
39            Frequency::Quarterly => "quarterly",
40        }
41    }
42}
43
44#[derive(Debug, Clone, Serialize, Deserialize)]
45#[serde(rename_all = "snake_case")]
46pub struct FinancialStatement {
47    pub symbol: String,
48    pub statement_type: String,
49    pub frequency: String,
50    #[serde(rename = "statement")]
51    pub statement: HashMap<String, HashMap<String, serde_json::Value>>,
52}
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57    use proptest::prelude::*;
58
59    // **Feature: crate-extraction, Property 1: Model Serialization Round-Trip**
60    // **Validates: Requirements 2.2**
61    proptest! {
62        #![proptest_config(ProptestConfig::with_cases(100))]
63
64        #[test]
65        fn statement_type_roundtrip(st in prop_oneof![
66            Just(StatementType::IncomeStatement),
67            Just(StatementType::BalanceSheet),
68            Just(StatementType::CashFlow),
69        ]) {
70            let json = serde_json::to_string(&st).unwrap();
71            let parsed: StatementType = serde_json::from_str(&json).unwrap();
72
73            prop_assert_eq!(st.as_str(), parsed.as_str());
74        }
75
76        #[test]
77        fn frequency_roundtrip(freq in prop_oneof![
78            Just(Frequency::Annual),
79            Just(Frequency::Quarterly),
80        ]) {
81            let json = serde_json::to_string(&freq).unwrap();
82            let parsed: Frequency = serde_json::from_str(&json).unwrap();
83
84            prop_assert_eq!(freq.as_str(), parsed.as_str());
85        }
86
87        #[test]
88        fn financial_statement_roundtrip(
89            symbol in "[A-Z]{1,5}",
90            statement_type in "income|balance|cashflow",
91            frequency in "annual|quarterly",
92        ) {
93            let statement = FinancialStatement {
94                symbol: symbol.clone(),
95                statement_type: statement_type.clone(),
96                frequency: frequency.clone(),
97                statement: HashMap::new(),
98            };
99
100            let json = serde_json::to_string(&statement).unwrap();
101            let parsed: FinancialStatement = serde_json::from_str(&json).unwrap();
102
103            prop_assert_eq!(statement.symbol, parsed.symbol);
104            prop_assert_eq!(statement.statement_type, parsed.statement_type);
105            prop_assert_eq!(statement.frequency, parsed.frequency);
106        }
107    }
108}