finance_query/models/quote/
formatted_value.rs1use serde::Serialize;
25
26#[derive(Debug, Clone, PartialEq, Serialize, Default)]
31#[serde(rename_all = "camelCase")]
32pub struct FormattedValue<T> {
33 #[serde(skip_serializing_if = "Option::is_none")]
35 pub fmt: Option<String>,
36
37 #[serde(skip_serializing_if = "Option::is_none")]
39 pub long_fmt: Option<String>,
40
41 #[serde(default)]
43 #[serde(skip_serializing_if = "Option::is_none")]
44 pub raw: Option<T>,
45}
46
47impl<'de, T: serde::Deserialize<'de>> serde::Deserialize<'de> for FormattedValue<T> {
50 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
51 where
52 D: serde::Deserializer<'de>,
53 {
54 #[derive(serde::Deserialize)]
55 #[serde(rename_all = "camelCase")]
56 struct Full<T> {
57 fmt: Option<String>,
58 long_fmt: Option<String>,
59 raw: Option<T>,
60 }
61
62 #[derive(serde::Deserialize)]
63 #[serde(untagged)]
64 enum Helper<T> {
65 Full(Full<T>),
66 Bare(T),
67 }
68
69 match Helper::<T>::deserialize(deserializer)? {
70 Helper::Full(f) => Ok(FormattedValue {
71 fmt: f.fmt,
72 long_fmt: f.long_fmt,
73 raw: f.raw,
74 }),
75 Helper::Bare(v) => Ok(FormattedValue {
76 fmt: None,
77 long_fmt: None,
78 raw: Some(v),
79 }),
80 }
81 }
82}
83
84impl<T> FormattedValue<T> {
85 pub fn new(raw: T) -> Self {
87 Self {
88 fmt: None,
89 long_fmt: None,
90 raw: Some(raw),
91 }
92 }
93
94 pub fn with_fmt(raw: T, fmt: String) -> Self {
96 Self {
97 fmt: Some(fmt),
98 long_fmt: None,
99 raw: Some(raw),
100 }
101 }
102
103 pub fn with_all(raw: T, fmt: String, long_fmt: String) -> Self {
105 Self {
106 fmt: Some(fmt),
107 long_fmt: Some(long_fmt),
108 raw: Some(raw),
109 }
110 }
111
112 pub fn value(&self) -> Option<&T> {
114 self.raw.as_ref()
115 }
116
117 pub fn formatted(&self) -> Option<&str> {
119 self.fmt.as_deref().or(self.long_fmt.as_deref())
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126
127 #[test]
128 fn test_deserialize_simple() {
129 let json = r#"{"fmt": "276.97", "raw": 276.97}"#;
130 let value: FormattedValue<f64> = serde_json::from_str(json).unwrap();
131 assert_eq!(value.raw, Some(276.97));
132 assert_eq!(value.fmt.as_deref(), Some("276.97"));
133 assert_eq!(value.long_fmt, None);
134 }
135
136 #[test]
137 fn test_deserialize_with_long_fmt() {
138 let json = r#"{"fmt": "14.78B", "longFmt": "14,776,353,000", "raw": 14776353000}"#;
139 let value: FormattedValue<i64> = serde_json::from_str(json).unwrap();
140 assert_eq!(value.raw, Some(14776353000));
141 assert_eq!(value.fmt.as_deref(), Some("14.78B"));
142 assert_eq!(value.long_fmt.as_deref(), Some("14,776,353,000"));
143 }
144
145 #[test]
146 fn test_formatted_helper() {
147 let value = FormattedValue::with_fmt(100.5, "100.50".to_string());
148 assert_eq!(value.formatted(), Some("100.50"));
149
150 let value = FormattedValue::new(100.5);
151 assert_eq!(value.formatted(), None);
152 }
153}
154
155#[cfg(test)]
156mod test_empty_object {
157 use super::*;
158
159 #[test]
160 fn test_empty_object_deserializes() {
161 let json = "{}";
162 let result: Result<FormattedValue<f64>, _> = serde_json::from_str(json);
163 assert!(
164 result.is_ok(),
165 "Empty object should deserialize: {:?}",
166 result.err()
167 );
168
169 let fv = result.unwrap();
170 assert_eq!(fv.raw, None);
171 assert_eq!(fv.fmt, None);
172 assert_eq!(fv.long_fmt, None);
173 }
174}