1use core::cmp::Ordering;
4use core::fmt;
5use core::hash::{Hash, Hasher};
6use core::marker::PhantomData;
7
8#[cfg(feature = "sea-orm")]
9use sea_orm::sea_query::{ArrayType, Nullable, ValueType, ValueTypeErr};
10#[cfg(feature = "sea-orm")]
11use sea_orm::{ColIdx, ColumnType, DbErr, QueryResult, TryFromU64, TryGetError, TryGetable, Value};
12
13pub type TmdbRepr = u32;
15
16#[derive(serde::Serialize, serde::Deserialize)]
18#[serde(transparent)]
19#[repr(transparent)]
20pub struct Id<T> {
21 id: TmdbRepr,
22 #[serde(skip_serializing, default)]
23 _phantom: PhantomData<T>,
24}
25
26impl<T> Clone for Id<T> {
28 fn clone(&self) -> Self {
29 *self
30 }
31}
32
33impl<T> Copy for Id<T> {}
35
36impl<T> PartialEq for Id<T> {
38 fn eq(&self, other: &Self) -> bool {
39 self.id == other.id
40 }
41}
42
43impl<T> Eq for Id<T> {}
45
46impl<T> PartialOrd for Id<T> {
48 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
49 Some(self.cmp(other))
50 }
51}
52
53impl<T> Ord for Id<T> {
55 fn cmp(&self, other: &Self) -> Ordering {
56 self.id.cmp(&other.id)
57 }
58}
59
60impl<T> Hash for Id<T> {
62 fn hash<H: Hasher>(&self, state: &mut H) {
63 self.id.hash(state);
64 }
65}
66
67impl<T> Id<T> {
68 pub fn from_raw(raw: TmdbRepr) -> Self {
70 Self {
71 id: raw,
72 _phantom: PhantomData,
73 }
74 }
75
76 pub fn into_raw(self) -> TmdbRepr {
78 self.id
79 }
80}
81
82impl<T> fmt::Debug for Id<T> {
83 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84 f.debug_struct("Id")
85 .field("T", &core::any::type_name::<T>())
86 .field("id", &self.id)
87 .finish()
88 }
89}
90
91#[cfg(feature = "sea-orm")]
92impl<T> ValueType for Id<T> {
93 fn try_from(v: Value) -> Result<Self, ValueTypeErr> {
94 <TmdbRepr as ValueType>::try_from(v).map(|id| Self {
95 id,
96 _phantom: PhantomData,
97 })
98 }
99
100 fn type_name() -> String {
101 format!("Id<{}>", &core::any::type_name::<T>())
102 }
103
104 fn array_type() -> ArrayType {
105 TmdbRepr::array_type()
106 }
107
108 fn column_type() -> ColumnType {
109 TmdbRepr::column_type()
110 }
111}
112
113#[cfg(feature = "sea-orm")]
114impl<T> From<Id<T>> for Value {
115 fn from(value: Id<T>) -> Self {
116 value.id.into()
117 }
118}
119
120#[cfg(feature = "sea-orm")]
121impl<T> TryGetable for Id<T> {
122 fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError> {
123 TmdbRepr::try_get_by(res, index).map(|id| Self {
124 id,
125 _phantom: PhantomData,
126 })
127 }
128}
129
130#[cfg(feature = "sea-orm")]
131impl<T> TryFromU64 for Id<T> {
132 fn try_from_u64(n: u64) -> Result<Self, DbErr> {
133 TmdbRepr::try_from_u64(n).map(|id| Self {
134 id,
135 _phantom: PhantomData,
136 })
137 }
138}
139
140#[cfg(feature = "sea-orm")]
141impl<T> Nullable for Id<T> {
142 fn null() -> Value {
143 TmdbRepr::null()
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 #[test]
150 #[cfg(feature = "sea-orm")]
151 fn test_sea_orm() {
152 use sea_orm::{
153 ActiveModelBehavior, DeriveEntityModel, DerivePrimaryKey, DeriveRelation, EntityTrait,
154 EnumIter, PrimaryKeyTrait,
155 };
156
157 use super::Id;
158
159 #[allow(dead_code)]
160 #[derive(Debug, Clone, PartialEq, Eq, DeriveEntityModel)]
161 #[sea_orm(table_name = "ids")]
162 pub struct Model {
163 #[sea_orm(primary_key, auto_increment = false)]
164 id: Id<Model>,
165 nullable: Option<Id<Model>>,
166 }
167
168 impl ActiveModelBehavior for ActiveModel {}
169
170 #[allow(dead_code)]
171 #[derive(Debug, EnumIter, DeriveRelation)]
172 pub enum Relation {}
173 }
174
175 #[test]
176 fn test_serde() {
177 use super::Id;
178
179 let id: Id<()> = Id::from_raw(1234);
180 serde_test::assert_tokens(&id, &[serde_test::Token::U32(1234)]);
181 }
182}
183
184pub use self::TmdbRepr as RawId;
186
187#[doc(hidden)]
188pub enum Collection {}
189pub type CollectionId = Id<Collection>;
191
192impl From<CollectionId> for flix_model::id::CollectionId {
193 fn from(value: CollectionId) -> Self {
194 Self::from_raw(value.into_raw().into())
195 }
196}
197
198impl TryFrom<flix_model::id::CollectionId> for CollectionId {
199 type Error = <RawId as TryFrom<flix_model::id::RawId>>::Error;
200
201 fn try_from(value: flix_model::id::CollectionId) -> Result<Self, Self::Error> {
202 value.into_raw().try_into().map(Self::from_raw)
203 }
204}
205
206#[doc(hidden)]
207pub enum Movie {}
208pub type MovieId = Id<Movie>;
210
211impl From<MovieId> for flix_model::id::MovieId {
212 fn from(value: MovieId) -> Self {
213 Self::from_raw(value.into_raw().into())
214 }
215}
216
217impl TryFrom<flix_model::id::MovieId> for MovieId {
218 type Error = <RawId as TryFrom<flix_model::id::RawId>>::Error;
219
220 fn try_from(value: flix_model::id::MovieId) -> Result<Self, Self::Error> {
221 value.into_raw().try_into().map(Self::from_raw)
222 }
223}
224
225#[doc(hidden)]
226pub enum Show {}
227pub type ShowId = Id<Show>;
229
230impl From<ShowId> for flix_model::id::ShowId {
231 fn from(value: ShowId) -> Self {
232 Self::from_raw(value.into_raw().into())
233 }
234}
235
236impl TryFrom<flix_model::id::ShowId> for ShowId {
237 type Error = <RawId as TryFrom<flix_model::id::RawId>>::Error;
238
239 fn try_from(value: flix_model::id::ShowId) -> Result<Self, Self::Error> {
240 value.into_raw().try_into().map(Self::from_raw)
241 }
242}