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}