use serde::Serialize;
#[derive(Debug, Clone, PartialEq, Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct FormattedValue<T> {
#[serde(skip_serializing_if = "Option::is_none")]
pub fmt: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub long_fmt: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub raw: Option<T>,
}
impl<'de, T: serde::Deserialize<'de>> serde::Deserialize<'de> for FormattedValue<T> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct Full<T> {
fmt: Option<String>,
long_fmt: Option<String>,
raw: Option<T>,
}
#[derive(serde::Deserialize)]
#[serde(untagged)]
enum Helper<T> {
Full(Full<T>),
Bare(T),
}
match Helper::<T>::deserialize(deserializer)? {
Helper::Full(f) => Ok(FormattedValue {
fmt: f.fmt,
long_fmt: f.long_fmt,
raw: f.raw,
}),
Helper::Bare(v) => Ok(FormattedValue {
fmt: None,
long_fmt: None,
raw: Some(v),
}),
}
}
}
impl<T> FormattedValue<T> {
pub fn new(raw: T) -> Self {
Self {
fmt: None,
long_fmt: None,
raw: Some(raw),
}
}
pub fn with_fmt(raw: T, fmt: String) -> Self {
Self {
fmt: Some(fmt),
long_fmt: None,
raw: Some(raw),
}
}
pub fn with_all(raw: T, fmt: String, long_fmt: String) -> Self {
Self {
fmt: Some(fmt),
long_fmt: Some(long_fmt),
raw: Some(raw),
}
}
pub fn value(&self) -> Option<&T> {
self.raw.as_ref()
}
pub fn formatted(&self) -> Option<&str> {
self.fmt.as_deref().or(self.long_fmt.as_deref())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_deserialize_simple() {
let json = r#"{"fmt": "276.97", "raw": 276.97}"#;
let value: FormattedValue<f64> = serde_json::from_str(json).unwrap();
assert_eq!(value.raw, Some(276.97));
assert_eq!(value.fmt.as_deref(), Some("276.97"));
assert_eq!(value.long_fmt, None);
}
#[test]
fn test_deserialize_with_long_fmt() {
let json = r#"{"fmt": "14.78B", "longFmt": "14,776,353,000", "raw": 14776353000}"#;
let value: FormattedValue<i64> = serde_json::from_str(json).unwrap();
assert_eq!(value.raw, Some(14776353000));
assert_eq!(value.fmt.as_deref(), Some("14.78B"));
assert_eq!(value.long_fmt.as_deref(), Some("14,776,353,000"));
}
#[test]
fn test_formatted_helper() {
let value = FormattedValue::with_fmt(100.5, "100.50".to_string());
assert_eq!(value.formatted(), Some("100.50"));
let value = FormattedValue::new(100.5);
assert_eq!(value.formatted(), None);
}
}
#[cfg(test)]
mod test_empty_object {
use super::*;
#[test]
fn test_empty_object_deserializes() {
let json = "{}";
let result: Result<FormattedValue<f64>, _> = serde_json::from_str(json);
assert!(
result.is_ok(),
"Empty object should deserialize: {:?}",
result.err()
);
let fv = result.unwrap();
assert_eq!(fv.raw, None);
assert_eq!(fv.fmt, None);
assert_eq!(fv.long_fmt, None);
}
}