1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
use serde::de::Unexpected;
use serde::Deserialize;
use serde::Deserializer;
use serde::Serialize;
use serde_repr::Deserialize_repr;
use serde_repr::Serialize_repr;
use smartstring::alias::String;

#[derive(Debug, Deserialize, Serialize)]
/// Represents a single title in IMDB; a "title" can be a show, an episode of a show, a movie, etc.  See [TitleType] for a list.
pub struct Title {
	/// ID from IMDB, with the leading "tt" removed
	pub imdb_id: u64,
	pub title_type: TitleType,
	pub primary_title: String,
	pub original_title: String,
	pub is_adult: bool,
	pub start_year: Option<u16>,
	pub end_year: Option<u16>,
	pub runtime_minutes: Option<u16>,
	pub genres: Vec<String>
}

impl From<TitleFromImdb> for Title /* {{{ */ {
	fn from(input: TitleFromImdb) -> Self {
		Self{
			imdb_id: input.imdb_id,
			title_type: input.title_type.into(),
			primary_title: input.primary_title,
			original_title: input.original_title,
			is_adult: input.is_adult != 0,
			start_year: input.start_year,
			end_year: input.end_year,
			runtime_minutes: input.runtime_minutes,
			genres: input.genres
		}
	}
} // }}}

#[derive(Clone, Copy, Debug, Deserialize_repr, Eq, PartialEq, Serialize_repr, strum::IntoStaticStr)]
#[repr(u8)]
/// Identifies the type of a [Title].  IMDB has more possible values that, because this crate is opinionated, we collapse down.
///
/// For example, "episode", "tvEpisode", and "tvPilot" all become TitleType::Episode.
pub enum TitleType {
	Short = 0,
	Movie = 1,
	Episode = 2,
	TVSeries = 3,
	TVShort = 4,
	TVSpecial = 5,
	Video = 6,
	VideoGame = 7,
	Audiobook = 8,
	RadioSeries = 9,
	RadioEpisode = 10
}

impl From<TitleTypeFromImdb> for TitleType /* {{{ */ {
	fn from(input: TitleTypeFromImdb) -> Self {
		match input {
			TitleTypeFromImdb::Short => Self::Short,
			TitleTypeFromImdb::Movie => Self::Movie,
			TitleTypeFromImdb::Episode => Self::Episode,
			TitleTypeFromImdb::TVSeries => Self::TVSeries,
			TitleTypeFromImdb::TVShort => Self::TVShort,
			TitleTypeFromImdb::TVSpecial => Self::TVSpecial,
			TitleTypeFromImdb::Video => Self::Video,
			TitleTypeFromImdb::VideoGame => Self::VideoGame,
			TitleTypeFromImdb::Audiobook => Self::Audiobook,
			TitleTypeFromImdb::RadioSeries => Self::RadioSeries,
			TitleTypeFromImdb::RadioEpisode => Self::RadioEpisode
		}
	}
} // }}}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct TitleFromImdb {
	#[serde(rename = "tconst", deserialize_with = "crate::util::parse_imdb_id")]
	pub imdb_id: u64,
	pub title_type: TitleTypeFromImdb,
	pub primary_title: String,
	pub original_title: String,
	pub is_adult: u8,
	#[serde(deserialize_with = "crate::util::parse_janky_tsv_option")]
	pub start_year: Option<u16>,
	#[serde(deserialize_with = "crate::util::parse_janky_tsv_option")]
	pub end_year: Option<u16>,
	#[serde(deserialize_with = "crate::util::parse_janky_tsv_option")]
	pub runtime_minutes: Option<u16>,
	#[serde(with = "serde_with::StringWithSeparator::<serde_with::CommaSeparator>")]
	pub genres: Vec<String>
}

#[derive(Debug, Eq, PartialEq)]
pub(crate) enum TitleTypeFromImdb {
	Short,
	Movie,
	Episode,
	TVSeries,
	TVShort,
	TVSpecial,
	Video,
	VideoGame,
	Audiobook,
	RadioSeries,
	RadioEpisode
}

impl<'de> Deserialize<'de> for TitleTypeFromImdb {
	fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
		let s: &str = Deserialize::deserialize(deserializer)?;
		Ok(match s {
			"short" => Self::Short,
			"episode" | "tvEpisode" | "tvPilot" => Self::Episode,
			"movie" | "tvMovie" => Self::Movie,
			"tvSeries" | "tvMiniSeries" => Self::TVSeries,
			"tvShort" => Self::TVShort,
			"tvSpecial" => Self::TVSpecial,
			"video" => Self::Video,
			"videoGame" => Self::VideoGame,
			"audiobook" => Self::Audiobook,
			"radioSeries" => Self::RadioSeries,
			"radioEpisode" => Self::RadioEpisode,
			s => return Err(serde::de::Error::invalid_value(Unexpected::Str(s), &"'short', 'episode', 'tvEpisode', 'tvPilot', 'movie', 'tvMovie', 'tvSeries', 'tvMiniSeries', 'tvShort', 'tvSpecial', 'video', 'videoGame', 'audiobook', 'radioSeries', 'radioEpisode'"))
		})
	}
}