use serde::{Deserialize, Serialize};
use crate::{MediaIds, StandardMediaObject};
pub const CHECKIN_URL: &str = "https://api.simkl.com/checkin";
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct CheckinEpisode {
#[serde(skip_serializing_if = "Option::is_none")]
pub season: Option<u32>,
pub number: u32,
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct ShowCheckinItem {
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub year: Option<u16>,
pub ids: MediaIds,
pub episode: CheckinEpisode,
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct CheckinPayload {
#[serde(skip_serializing_if = "Option::is_none")]
pub movie: Option<StandardMediaObject>,
#[serde(skip_serializing_if = "Option::is_none")]
pub show: Option<ShowCheckinItem>,
}
impl CheckinPayload {
pub fn movie(item: StandardMediaObject) -> Self {
Self {
movie: Some(item),
show: None,
}
}
pub fn show(item: ShowCheckinItem) -> Self {
Self {
movie: None,
show: Some(item),
}
}
}
#[derive(Default, Debug, Clone, Deserialize)]
pub struct CheckinResponse {
pub movie: Option<StandardMediaObject>,
pub show: Option<StandardMediaObject>,
pub anime: Option<StandardMediaObject>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct CheckinConflict {
pub expires_at: String,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::MediaIds;
fn guardians() -> StandardMediaObject {
StandardMediaObject {
title: "Guardians of the Galaxy".to_string(),
year: 2014,
ids: MediaIds {
slug: Some("guardians-of-the-galaxy-2014".to_string()),
imdb: Some("tt2015381".to_string()),
tmdb: Some(118340),
..MediaIds::default()
},
seasons: None,
episodes: None,
}
}
fn breaking_bad_episode() -> ShowCheckinItem {
ShowCheckinItem {
title: Some("Breaking Bad".to_string()),
year: Some(2008),
ids: MediaIds {
tvdb: Some(81189),
imdb: Some("tt0903747".to_string()),
..MediaIds::default()
},
episode: CheckinEpisode {
season: Some(2),
number: 7,
},
}
}
#[test]
fn movie_payload_serializes_only_movie_key() {
let payload = CheckinPayload::movie(guardians());
let json = serde_json::to_string(&payload).expect("serialization failed");
assert!(json.contains("\"movie\""), "expected 'movie' key in JSON");
assert!(
!json.contains("\"show\""),
"unexpected 'show' key in movie JSON"
);
assert!(
json.contains("Guardians of the Galaxy"),
"expected movie title in JSON"
);
assert!(json.contains("tt2015381"), "expected imdb id in JSON");
}
#[test]
fn show_payload_serializes_only_show_key() {
let payload = CheckinPayload::show(breaking_bad_episode());
let json = serde_json::to_string(&payload).expect("serialization failed");
assert!(json.contains("\"show\""), "expected 'show' key in JSON");
assert!(
!json.contains("\"movie\""),
"unexpected 'movie' key in show JSON"
);
assert!(json.contains("Breaking Bad"), "expected show title in JSON");
assert!(
json.contains("\"episode\""),
"expected 'episode' key in JSON"
);
assert!(
json.contains("\"season\""),
"expected 'season' key in episode JSON"
);
assert!(
json.contains("\"number\""),
"expected 'number' key in episode JSON"
);
}
#[test]
fn episode_skips_absent_season() {
let episode = CheckinEpisode {
season: None,
number: 3,
};
let json = serde_json::to_string(&episode).expect("serialization failed");
assert!(
!json.contains("\"season\""),
"absent season should be skipped"
);
assert!(json.contains("\"number\""), "number must always be present");
}
#[test]
fn checkin_url_is_correct() {
assert_eq!(CHECKIN_URL, "https://api.simkl.com/checkin");
}
}