diff --git a/crates/oxidize-common/Cargo.toml b/crates/oxidize-common/Cargo.toml index 45c71dee..ea16ff46 100644 --- a/crates/oxidize-common/Cargo.toml +++ b/crates/oxidize-common/Cargo.toml @@ -6,7 +6,7 @@ publish = false [dependencies] chrono = { workspace = true } -diesel = { workspace = true } +diesel = { workspace = true, features = ["sqlite"] } futures-util = { version = "0.3.28", default-features = false, features = ["sink", "alloc"] } serde = { workspace = true } serde_json = { workspace = true } diff --git a/crates/oxidize-common/src/models/spotify.rs b/crates/oxidize-common/src/models/spotify.rs index fb330106..bae53092 100644 --- a/crates/oxidize-common/src/models/spotify.rs +++ b/crates/oxidize-common/src/models/spotify.rs @@ -1,6 +1,62 @@ //! All Spotify API endpoint response object -//! -//! Copied under the MIT license from: . + +// Copied under the MIT license from: . +// +// Copyright (c) 2015 Vincent Prouillet + +use serde::de::{Deserialize, Deserializer, Error}; + +use serde_json::Number; + +#[inline] +pub fn f64_to_u32(x: f64) -> Option { + let y = x as u32; + + if y as f64 == x { + Some(y) + } else { + None + } +} + +pub fn deserialize_option_u32<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + let Some(number) = >::deserialize(deserializer)? else { + return Ok(None); + }; + + if let Some(n) = number.as_u64() { + let Ok(n) = u32::try_from(n) else { + return Err(D::Error::custom(format_args!( + "Number {n} is out of numerical bounds 0-{}", + u32::MAX + ))); + }; + + return Ok(Some(n)); + } + + if let Some(n) = number.as_i64() { + let Ok(n) = u32::try_from(n) else { + return Err(D::Error::custom(format_args!( + "Number {n} is out of numerical bounds 0-{}", + u32::MAX + ))); + }; + + return Ok(Some(n)); + } + + if let Some(n) = number.as_f64().and_then(f64_to_u32) { + return Ok(Some(n)); + } + + Err(D::Error::custom(format_args!( + "Number {number} is not a valid u32" + ))) +} pub mod album; pub mod artist; diff --git a/crates/oxidize-common/src/models/spotify/image.rs b/crates/oxidize-common/src/models/spotify/image.rs index b501a6e1..c8d7d3a2 100644 --- a/crates/oxidize-common/src/models/spotify/image.rs +++ b/crates/oxidize-common/src/models/spotify/image.rs @@ -2,10 +2,27 @@ use serde::{Deserialize, Serialize}; +use super::deserialize_option_u32; + ///[image object](https://developer.spotify.com/web-api/object-model/#image-object) #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Image { + #[serde(deserialize_with = "deserialize_option_u32")] pub height: Option, pub url: String, + #[serde(deserialize_with = "deserialize_option_u32")] pub width: Option, } + +#[test] +fn test_deserialize_float_width_height() { + let json = r#"{"height": 640.0, "url": "https://i.scdn.co/image/ab67616d0000b273f3e3e3e3e3e3e3e3e3e3e3e3", "width": 640.0}"#; + let image: Image = serde_json::from_str(json).unwrap(); + assert_eq!(image.height, Some(640)); + assert_eq!(image.width, Some(640)); + + let json = r#"{"height": 640, "url": "https://i.scdn.co/image/ab67616d0000b273f3e3e3e3e3e3e3e3e3e3e3e3", "width": 640}"#; + let image: Image = serde_json::from_str(json).unwrap(); + assert_eq!(image.height, Some(640)); + assert_eq!(image.width, Some(640)); +}