Skip to main content

finance_query/models/
format.rs

1//! Compile-time format type parameters for `FormattedValue`-bearing structs.
2//!
3//! Structs like [`Quote`](crate::Quote) carry a format type parameter `F: Format`
4//! that controls what type each numeric field holds:
5//!
6//! | `F`      | `F::Value<f64>`         | Access pattern            |
7//! |----------|-------------------------|---------------------------|
8//! | [`Both`] | `FormattedValue<f64>`   | `.raw` / `.fmt` / `.long_fmt` |
9//! | [`Raw`]  | `f64`                   | direct — no unwrapping (**default**) |
10//! | [`Pretty`] | `String`              | human-readable string     |
11//!
12//! # Quick start
13//!
14//! ```no_run
15//! use finance_query::{Ticker, format};
16//!
17//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
18//! // quote() returns Quote<Raw> by default — fields are plain f64/i64
19//! let quote: finance_query::Quote<format::Raw> = Ticker::new("AAPL").await?.quote().await?;
20//! let price: Option<f64> = quote.regular_market_price;
21//! # Ok(())
22//! # }
23//! ```
24
25use crate::models::quote::FormattedValue;
26
27mod sealed {
28    /// Blocks external crates from implementing [`Format`](super::Format);
29    /// only this module can name `Sealed`, so it can only be satisfied here.
30    pub trait Sealed {}
31}
32
33/// Marker trait that controls how [`FormattedValue`](crate::FormattedValue) fields are typed.
34///
35/// Sealed — only [`Both`], [`Raw`], and [`Pretty`] implement this trait.
36pub trait Format: sealed::Sealed + Clone + std::fmt::Debug + PartialEq + 'static {
37    /// The concrete field type for a numeric value of type `T`.
38    type Value<T: Clone + std::fmt::Debug + PartialEq + serde::Serialize + for<'de> serde::Deserialize<'de>>: Clone
39        + std::fmt::Debug
40        + PartialEq
41        + serde::Serialize
42        + for<'de> serde::Deserialize<'de>;
43
44    /// Extract the raw numeric value from a `Value<T>`, if available.
45    ///
46    /// Returns `Some(T)` for [`Both`] (from `.raw`) and [`Raw`] (the value itself),
47    /// `None` for [`Pretty`] (no numeric representation is stored).
48    fn raw_from<
49        T: Clone + std::fmt::Debug + PartialEq + serde::Serialize + for<'de> serde::Deserialize<'de>,
50    >(
51        value: &Self::Value<T>,
52    ) -> Option<T>;
53}
54
55/// Full format — fields hold `FormattedValue<T>` with `raw`, `fmt`, and `long_fmt`.
56///
57/// Obtain via [`Ticker::quote`](crate::Ticker::quote) with the default `Both`
58/// format parameter. This is the form that can be deserialized directly from
59/// Yahoo Finance JSON.
60#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
61pub struct Both;
62
63/// Raw format — fields hold `T` directly (e.g. `f64`, `i64`). **This is the default.**
64///
65/// Obtain via [`Ticker::quote()`](crate::Ticker::quote) (the default return type),
66/// [`Quote::into_raw`](crate::Quote::into_raw), or
67/// [`Quote::as_raw`](crate::Quote::as_raw). No `Option`-wrapping of the value itself;
68/// the `Option` at the field level reflects missing data from the API.
69#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
70pub struct Raw;
71
72/// Pretty format — fields hold an `Option<String>` with the human-readable representation.
73///
74/// Obtain via [`Quote::into_pretty`](crate::Quote::into_pretty).
75/// Falls back to `long_fmt` when `fmt` is absent.
76#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
77pub struct Pretty;
78
79impl sealed::Sealed for Both {}
80impl sealed::Sealed for Raw {}
81impl sealed::Sealed for Pretty {}
82
83impl Format for Both {
84    type Value<
85        T: Clone + std::fmt::Debug + PartialEq + serde::Serialize + for<'de> serde::Deserialize<'de>,
86    > = FormattedValue<T>;
87
88    fn raw_from<
89        T: Clone + std::fmt::Debug + PartialEq + serde::Serialize + for<'de> serde::Deserialize<'de>,
90    >(
91        value: &FormattedValue<T>,
92    ) -> Option<T> {
93        value.raw.clone()
94    }
95}
96
97impl Format for Raw {
98    type Value<
99        T: Clone + std::fmt::Debug + PartialEq + serde::Serialize + for<'de> serde::Deserialize<'de>,
100    > = T;
101
102    fn raw_from<
103        T: Clone + std::fmt::Debug + PartialEq + serde::Serialize + for<'de> serde::Deserialize<'de>,
104    >(
105        value: &T,
106    ) -> Option<T> {
107        Some(value.clone())
108    }
109}
110
111impl Format for Pretty {
112    type Value<
113        T: Clone + std::fmt::Debug + PartialEq + serde::Serialize + for<'de> serde::Deserialize<'de>,
114    > = String;
115
116    fn raw_from<
117        T: Clone + std::fmt::Debug + PartialEq + serde::Serialize + for<'de> serde::Deserialize<'de>,
118    >(
119        _value: &String,
120    ) -> Option<T> {
121        None
122    }
123}