Skip to main content

fmp_rust_sdk/
models.rs

1//! Typed responses from the FMP Stable API.
2//!
3//! Identifying fields are required, dates are parsed, and unrelated response
4//! fields are ignored. Financial values use `f64` because FMP returns both integer
5//! amounts and fractional values; use your own decimal model with `Client::get`
6//! when exact decimal arithmetic is required.
7
8mod company;
9mod events;
10mod financials;
11mod growth;
12mod market;
13mod metrics;
14mod prices;
15mod quotes;
16mod ttm;
17
18pub use company::*;
19pub use events::*;
20pub use financials::*;
21pub use growth::*;
22pub use market::*;
23pub use metrics::*;
24pub use prices::*;
25pub use quotes::*;
26pub use ttm::*;
27
28use chrono::{NaiveDate, NaiveDateTime};
29use serde::{Deserialize, Deserializer, Serializer, de::Error};
30
31fn non_empty_string<'de, D>(deserializer: D) -> Result<String, D::Error>
32where
33    D: Deserializer<'de>,
34{
35    let value = String::deserialize(deserializer)?;
36    if value.trim().is_empty() {
37        return Err(D::Error::custom("required identifier must not be empty"));
38    }
39    Ok(value)
40}
41
42/// FMP represents unknown corporate-action dates with either null or an empty string.
43fn optional_date<'de, D>(deserializer: D) -> Result<Option<NaiveDate>, D::Error>
44where
45    D: Deserializer<'de>,
46{
47    match Option::<String>::deserialize(deserializer)? {
48        None => Ok(None),
49        Some(value) if value.is_empty() => Ok(None),
50        Some(value) => NaiveDate::parse_from_str(&value, "%Y-%m-%d")
51            .map(Some)
52            .map_err(D::Error::custom),
53    }
54}
55
56/// Provider-local timestamps contain no UTC offset; do not interpret them as UTC.
57mod timestamp {
58    use super::*;
59
60    pub fn deserialize<'de, D>(deserializer: D) -> Result<NaiveDateTime, D::Error>
61    where
62        D: Deserializer<'de>,
63    {
64        let value = String::deserialize(deserializer)?;
65        NaiveDateTime::parse_from_str(&value, "%Y-%m-%d %H:%M:%S").map_err(D::Error::custom)
66    }
67
68    pub fn serialize<S>(value: &NaiveDateTime, serializer: S) -> Result<S::Ok, S::Error>
69    where
70        S: Serializer,
71    {
72        serializer.serialize_str(&value.format("%Y-%m-%d %H:%M:%S").to_string())
73    }
74}
75
76mod optional_timestamp {
77    use super::*;
78
79    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<NaiveDateTime>, D::Error>
80    where
81        D: Deserializer<'de>,
82    {
83        Option::<String>::deserialize(deserializer)?
84            .map(|value| {
85                NaiveDateTime::parse_from_str(&value, "%Y-%m-%d %H:%M:%S").map_err(D::Error::custom)
86            })
87            .transpose()
88    }
89
90    pub fn serialize<S>(value: &Option<NaiveDateTime>, serializer: S) -> Result<S::Ok, S::Error>
91    where
92        S: Serializer,
93    {
94        match value {
95            Some(value) => {
96                serializer.serialize_some(&value.format("%Y-%m-%d %H:%M:%S").to_string())
97            }
98            None => serializer.serialize_none(),
99        }
100    }
101}