use serde::{Deserialize, Serialize};
use super::common::{ExternalUrls, Followers, Image};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserPublic {
pub display_name: Option<String>,
pub external_urls: Option<ExternalUrls>,
pub followers: Option<Followers>,
pub href: Option<String>,
pub id: String,
pub images: Option<Vec<Image>>,
#[serde(rename = "type")]
pub item_type: String,
pub uri: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserPrivate {
pub country: Option<String>,
pub display_name: Option<String>,
pub email: Option<String>,
pub explicit_content: Option<ExplicitContent>,
pub external_urls: Option<ExternalUrls>,
pub followers: Option<Followers>,
pub href: Option<String>,
pub id: String,
pub images: Option<Vec<Image>>,
pub product: Option<String>,
#[serde(rename = "type")]
pub item_type: String,
pub uri: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExplicitContent {
pub filter_enabled: Option<bool>,
pub filter_locked: Option<bool>,
}
impl UserPrivate {
pub fn is_premium(&self) -> bool {
self.product.as_deref() == Some("premium")
}
pub fn image_url(&self) -> Option<&str> {
self.images
.as_ref()
.and_then(|imgs| imgs.first())
.map(|img| img.url.as_str())
}
}
impl UserPublic {
pub fn image_url(&self) -> Option<&str> {
self.images
.as_ref()
.and_then(|imgs| imgs.first())
.map(|img| img.url.as_str())
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TopTracksResponse {
pub href: Option<String>,
pub limit: Option<u32>,
pub next: Option<String>,
pub offset: Option<u32>,
pub previous: Option<String>,
pub total: Option<u32>,
pub items: Vec<super::track::Track>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TopArtistsResponse {
pub href: Option<String>,
pub limit: Option<u32>,
pub next: Option<String>,
pub offset: Option<u32>,
pub previous: Option<String>,
pub total: Option<u32>,
pub items: Vec<super::artist::Artist>,
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn user_public_deserializes() {
let json = json!({
"id": "user123",
"type": "user",
"uri": "spotify:user:user123",
"display_name": "John Doe"
});
let user: UserPublic = serde_json::from_value(json).unwrap();
assert_eq!(user.id, "user123");
assert_eq!(user.display_name, Some("John Doe".to_string()));
}
#[test]
fn user_public_image_url() {
let json = json!({
"id": "user123",
"type": "user",
"uri": "spotify:user:user123",
"images": [{"url": "https://profile.jpg", "height": 300, "width": 300}]
});
let user: UserPublic = serde_json::from_value(json).unwrap();
assert_eq!(user.image_url(), Some("https://profile.jpg"));
}
#[test]
fn user_public_image_url_none_when_empty() {
let json = json!({
"id": "user123",
"type": "user",
"uri": "spotify:user:user123"
});
let user: UserPublic = serde_json::from_value(json).unwrap();
assert!(user.image_url().is_none());
}
#[test]
fn user_private_deserializes() {
let json = json!({
"id": "user123",
"type": "user",
"uri": "spotify:user:user123",
"email": "user@example.com",
"product": "premium",
"country": "US"
});
let user: UserPrivate = serde_json::from_value(json).unwrap();
assert_eq!(user.id, "user123");
assert_eq!(user.email, Some("user@example.com".to_string()));
assert!(user.is_premium());
}
#[test]
fn user_private_is_premium_false_for_free() {
let json = json!({
"id": "user123",
"type": "user",
"uri": "spotify:user:user123",
"product": "free"
});
let user: UserPrivate = serde_json::from_value(json).unwrap();
assert!(!user.is_premium());
}
#[test]
fn user_private_is_premium_false_when_none() {
let json = json!({
"id": "user123",
"type": "user",
"uri": "spotify:user:user123"
});
let user: UserPrivate = serde_json::from_value(json).unwrap();
assert!(!user.is_premium());
}
#[test]
fn explicit_content_deserializes() {
let json = json!({
"filter_enabled": true,
"filter_locked": false
});
let explicit: ExplicitContent = serde_json::from_value(json).unwrap();
assert_eq!(explicit.filter_enabled, Some(true));
assert_eq!(explicit.filter_locked, Some(false));
}
#[test]
fn top_tracks_response_deserializes() {
let json = json!({
"items": [],
"total": 50,
"limit": 20,
"offset": 0
});
let resp: TopTracksResponse = serde_json::from_value(json).unwrap();
assert!(resp.items.is_empty());
assert_eq!(resp.total, Some(50));
}
#[test]
fn top_artists_response_deserializes() {
let json = json!({
"items": [],
"total": 50,
"limit": 20,
"offset": 0
});
let resp: TopArtistsResponse = serde_json::from_value(json).unwrap();
assert!(resp.items.is_empty());
assert_eq!(resp.total, Some(50));
}
#[test]
fn user_private_image_url_returns_first() {
let json = json!({
"id": "user123",
"type": "user",
"uri": "spotify:user:user123",
"images": [
{"url": "https://first.jpg", "height": 640, "width": 640},
{"url": "https://second.jpg", "height": 300, "width": 300}
]
});
let user: UserPrivate = serde_json::from_value(json).unwrap();
assert_eq!(user.image_url(), Some("https://first.jpg"));
}
#[test]
fn user_private_image_url_none_when_empty() {
let json = json!({
"id": "user123",
"type": "user",
"uri": "spotify:user:user123"
});
let user: UserPrivate = serde_json::from_value(json).unwrap();
assert!(user.image_url().is_none());
}
}