use std::{fmt, str::FromStr};
use serde::{Deserialize, Serialize};
pub mod anime;
pub mod auth;
pub mod calendar;
pub mod checkin;
pub mod error;
pub mod images;
pub mod movie;
pub mod pagination;
pub mod pin;
pub mod rate_limit;
pub mod ratings;
pub mod redirect;
pub mod request;
pub mod response;
pub mod search;
pub mod show;
pub mod sync;
pub mod user;
pub const API_URL: &str = "https://api.simkl.com";
pub const OAUTH_URL: &str = "https://simkl.com/oauth/authorize";
pub const TOKEN_URL: &str = "https://api.simkl.com/oauth/token";
#[derive(Debug, Copy, Clone, Default)]
pub struct Extended {
pub full: bool,
pub title: bool,
pub slug: bool,
pub overview: bool,
pub metadata: bool,
pub theater: bool,
pub genres: bool,
pub tmdb: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ParseMediaTypeError(pub String);
impl fmt::Display for ParseMediaTypeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "unknown media type: {:?}", self.0)
}
}
impl std::error::Error for ParseMediaTypeError {}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
#[repr(u8)]
pub enum MediaType {
Movie,
Show,
Anime,
Episode,
}
impl fmt::Display for MediaType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
MediaType::Movie => "movie",
MediaType::Show => "show",
MediaType::Anime => "anime",
MediaType::Episode => "episode",
};
write!(f, "{}", s)
}
}
impl FromStr for MediaType {
type Err = ParseMediaTypeError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"movie" => Ok(MediaType::Movie),
"show" => Ok(MediaType::Show),
"anime" => Ok(MediaType::Anime),
"episode" => Ok(MediaType::Episode),
_ => Err(ParseMediaTypeError(s.to_owned())),
}
}
}
impl MediaType {
pub fn as_str(&self) -> &'static str {
match self {
MediaType::Movie => "movie",
MediaType::Show => "show",
MediaType::Anime => "anime",
MediaType::Episode => "episode",
}
}
}
pub fn get_extended_parameter(extended: Extended) -> Result<String, &'static str> {
if extended.full
&& (extended.title
|| extended.slug
|| extended.overview
|| extended.metadata
|| extended.theater
|| extended.genres
|| extended.tmdb)
{
return Err("extended cannot be full and have another parameter");
}
let mut selected: Vec<&str> = Vec::with_capacity(8);
if extended.full {
selected.push("full");
}
if extended.title {
selected.push("title");
}
if extended.slug {
selected.push("slug");
}
if extended.overview {
selected.push("overview");
}
if extended.metadata {
selected.push("metadata");
}
if extended.theater {
selected.push("theater");
}
if extended.genres {
selected.push("genres");
}
if extended.tmdb {
selected.push("tmdb");
}
Ok(selected.join(","))
}
pub fn get_pagination_parameter(page: u16, limit: u16) -> String {
let mut result = String::with_capacity(32);
result.push_str("?page=");
result.push_str(&page.to_string());
result.push_str("&limit=");
result.push_str(&limit.to_string());
result
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Default)]
pub struct MediaIds {
pub simkl: Option<u32>,
pub slug: Option<String>,
pub imdb: Option<String>,
pub tmdb: Option<u32>,
pub mal: Option<u32>,
pub anilist: Option<u32>,
pub anidb: Option<u32>,
pub tvdb: Option<u32>,
pub kitsu: Option<u32>,
}
impl MediaIds {
pub fn new() -> Self {
Self::default()
}
pub fn with_simkl(mut self, id: u32) -> Self {
self.simkl = Some(id);
self
}
pub fn with_imdb(mut self, id: String) -> Self {
self.imdb = Some(id);
self
}
pub fn with_tmdb(mut self, id: u32) -> Self {
self.tmdb = Some(id);
self
}
pub fn with_mal(mut self, id: u32) -> Self {
self.mal = Some(id);
self
}
pub fn has_any_id(&self) -> bool {
self.simkl.is_some()
|| self.imdb.is_some()
|| self.tmdb.is_some()
|| self.mal.is_some()
|| self.anilist.is_some()
|| self.anidb.is_some()
|| self.tvdb.is_some()
|| self.kitsu.is_some()
}
pub fn primary_id(&self) -> Option<String> {
if let Some(id) = self.simkl {
Some(id.to_string())
} else if let Some(ref id) = self.imdb {
Some(id.clone())
} else {
self.tmdb.map(|id| id.to_string())
}
}
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SeasonEpisode {
pub number: u16,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Season {
pub number: u16,
pub episodes: Vec<SeasonEpisode>,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct StandardMediaObject {
pub title: String,
pub year: u16,
pub ids: MediaIds,
pub seasons: Option<Vec<Season>>,
pub episodes: Option<Vec<SeasonEpisode>>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct Episode {
pub title: String,
pub year: Option<u32>,
pub released: Option<String>,
pub pk: Option<u32>,
pub ids: Option<MediaIds>,
pub season: u32,
pub episode: u32,
pub watched: Option<bool>,
pub last_watched_at: Option<String>,
pub watchers: Option<u32>,
pub plays: Option<u32>,
pub img: Option<String>,
pub overview: Option<String>,
pub rating: Option<f32>,
pub votes: Option<u32>,
pub runtime: Option<u32>,
pub user_rating: Option<u8>,
}
pub fn get_auth_url(client_id: &str, redirect_url: &str) -> String {
let mut result = String::with_capacity(128);
result.push_str(OAUTH_URL);
result.push_str("?response_type=code&redirect_uri=");
result.push_str(redirect_url);
result.push_str("&client_id=");
result.push_str(client_id);
result
}
#[derive(Default, Debug, Clone, PartialEq, Deserialize)]
pub struct Rank {
pub r#type: String,
pub votes: u32,
}
#[derive(Default, Debug, Clone, PartialEq, Deserialize)]
pub struct Rating {
pub rating: Option<f32>,
pub votes: Option<u32>,
}
#[derive(Default, Debug, Clone, PartialEq, Deserialize)]
pub struct Ratings {
pub id: u16,
pub link: String,
pub rank: Rank,
pub simkl: Rating,
pub imdb: Option<Rating>,
pub has_trailer: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AirInfo {
pub day: Option<String>,
pub time: Option<String>,
pub timezone: Option<String>,
}