1pub mod collections {
6 use flix_model::id::CollectionId;
7
8 use sea_orm::entity::prelude::*;
9
10 #[sea_orm::model]
12 #[derive(Debug, Clone, DeriveEntityModel)]
13 #[sea_orm(table_name = "flix_info_collections")]
14 pub struct Model {
15 #[sea_orm(column_type = "Integer", primary_key, nullable, auto_increment = false)]
17 pub id: CollectionId,
18 #[sea_orm(indexed)]
20 pub title: String,
21 pub overview: String,
23 }
24
25 impl ActiveModelBehavior for ActiveModel {}
26}
27
28pub mod movies {
30 use flix_model::id::MovieId;
31
32 use chrono::NaiveDate;
33 use sea_orm::entity::prelude::*;
34
35 #[sea_orm::model]
37 #[derive(Debug, Clone, DeriveEntityModel)]
38 #[sea_orm(table_name = "flix_info_movies")]
39 pub struct Model {
40 #[sea_orm(column_type = "Integer", primary_key, nullable, auto_increment = false)]
42 pub id: MovieId,
43 #[sea_orm(indexed)]
45 pub title: String,
46 pub tagline: String,
48 pub overview: String,
50 #[sea_orm(indexed)]
52 pub date: NaiveDate,
53 }
54
55 impl ActiveModelBehavior for ActiveModel {}
56}
57
58pub mod shows {
60 use flix_model::id::ShowId;
61
62 use chrono::NaiveDate;
63 use sea_orm::entity::prelude::*;
64
65 #[sea_orm::model]
67 #[derive(Debug, Clone, DeriveEntityModel)]
68 #[sea_orm(table_name = "flix_info_shows")]
69 pub struct Model {
70 #[sea_orm(column_type = "Integer", primary_key, nullable, auto_increment = false)]
72 pub id: ShowId,
73 #[sea_orm(indexed)]
75 pub title: String,
76 pub tagline: String,
78 pub overview: String,
80 #[sea_orm(indexed)]
82 pub date: NaiveDate,
83
84 #[sea_orm(has_many, on_update = "Cascade", on_delete = "Cascade")]
86 pub seasons: HasMany<super::seasons::Entity>,
87 #[sea_orm(has_many, on_update = "Cascade", on_delete = "Cascade")]
89 pub episodes: HasMany<super::episodes::Entity>,
90 }
91
92 impl ActiveModelBehavior for ActiveModel {}
93}
94
95pub mod seasons {
97 use flix_model::id::ShowId;
98 use flix_model::numbers::SeasonNumber;
99
100 use chrono::NaiveDate;
101 use sea_orm::entity::prelude::*;
102
103 #[sea_orm::model]
105 #[derive(Debug, Clone, DeriveEntityModel)]
106 #[sea_orm(table_name = "flix_info_seasons")]
107 pub struct Model {
108 #[sea_orm(primary_key, auto_increment = false)]
110 pub show_id: ShowId,
111 #[sea_orm(primary_key, auto_increment = false)]
113 pub season_number: SeasonNumber,
114 pub title: String,
116 pub overview: String,
118 #[sea_orm(indexed)]
120 pub date: NaiveDate,
121
122 #[sea_orm(belongs_to, from = "show_id", to = "id")]
124 pub show: HasOne<super::shows::Entity>,
125 #[sea_orm(has_many, on_update = "Cascade", on_delete = "Cascade")]
127 pub episodes: HasMany<super::episodes::Entity>,
128 }
129
130 impl ActiveModelBehavior for ActiveModel {}
131}
132
133pub mod episodes {
135 use flix_model::id::ShowId;
136 use flix_model::numbers::{EpisodeNumber, SeasonNumber};
137
138 use chrono::NaiveDate;
139 use sea_orm::entity::prelude::*;
140
141 #[sea_orm::model]
143 #[derive(Debug, Clone, DeriveEntityModel)]
144 #[sea_orm(table_name = "flix_info_episodes")]
145 pub struct Model {
146 #[sea_orm(primary_key, auto_increment = false)]
148 pub show_id: ShowId,
149 #[sea_orm(primary_key, auto_increment = false)]
151 pub season_number: SeasonNumber,
152 #[sea_orm(primary_key, auto_increment = false)]
154 pub episode_number: EpisodeNumber,
155 pub title: String,
157 pub overview: String,
159 #[sea_orm(indexed)]
161 pub date: NaiveDate,
162
163 #[sea_orm(belongs_to, from = "show_id", to = "id")]
165 pub show: HasOne<super::shows::Entity>,
166 #[sea_orm(
168 belongs_to,
169 from = "(show_id, season_number)",
170 to = "(show_id, season_number)"
171 )]
172 pub season: HasOne<super::seasons::Entity>,
173 }
174
175 impl ActiveModelBehavior for ActiveModel {}
176}
177
178#[cfg(test)]
180pub mod test {
181 macro_rules! make_info_collection {
182 ($db:expr, $id:literal) => {
183 $crate::entity::info::collections::ActiveModel {
184 id: Set(::flix_model::id::CollectionId::from_raw($id)),
185 title: Set(::std::string::String::new()),
186 overview: Set(::std::string::String::new()),
187 }
188 .insert($db)
189 .await
190 .expect("insert");
191 };
192 }
193 pub(crate) use make_info_collection;
194
195 macro_rules! make_info_movie {
196 ($db:expr, $id:literal) => {
197 $crate::entity::info::movies::ActiveModel {
198 id: Set(::flix_model::id::MovieId::from_raw($id)),
199 title: Set(::std::string::String::new()),
200 tagline: Set(::std::string::String::new()),
201 overview: Set(::std::string::String::new()),
202 date: Set(::chrono::NaiveDate::from_yo_opt(1, 1).expect("from_yo_opt")),
203 }
204 .insert($db)
205 .await
206 .expect("insert");
207 };
208 }
209 pub(crate) use make_info_movie;
210
211 macro_rules! make_info_show {
212 ($db:expr, $id:literal) => {
213 $crate::entity::info::shows::ActiveModel {
214 id: Set(::flix_model::id::ShowId::from_raw($id)),
215 title: Set(::std::string::String::new()),
216 tagline: Set(::std::string::String::new()),
217 overview: Set(::std::string::String::new()),
218 date: Set(::chrono::NaiveDate::from_yo_opt(1, 1).expect("from_yo_opt")),
219 }
220 .insert($db)
221 .await
222 .expect("insert");
223 };
224 }
225 pub(crate) use make_info_show;
226
227 macro_rules! make_info_season {
228 ($db:expr, $show:literal, $season:literal) => {
229 $crate::entity::info::seasons::ActiveModel {
230 show_id: Set(::flix_model::id::ShowId::from_raw($show)),
231 season_number: Set($season),
232 title: Set(::std::string::String::new()),
233 overview: Set(::std::string::String::new()),
234 date: Set(::chrono::NaiveDate::from_yo_opt(1, 1).expect("from_yo_opt")),
235 }
236 .insert($db)
237 .await
238 .expect("insert");
239 };
240 }
241 pub(crate) use make_info_season;
242
243 macro_rules! make_info_episode {
244 ($db:expr, $show:literal, $season:literal, $episode:literal) => {
245 $crate::entity::info::episodes::ActiveModel {
246 show_id: Set(::flix_model::id::ShowId::from_raw($show)),
247 season_number: Set($season),
248 episode_number: Set($episode),
249 title: Set(::std::string::String::new()),
250 overview: Set(::std::string::String::new()),
251 date: Set(::chrono::NaiveDate::from_yo_opt(1, 1).expect("from_yo_opt")),
252 }
253 .insert($db)
254 .await
255 .expect("insert");
256 };
257 }
258 pub(crate) use make_info_episode;
259}
260
261#[cfg(test)]
262mod tests {
263 use flix_model::id::{CollectionId, MovieId, ShowId};
264
265 use chrono::NaiveDate;
266 use sea_orm::ActiveValue::{NotSet, Set};
267 use sea_orm::entity::prelude::*;
268 use sea_orm::sqlx::error::ErrorKind;
269
270 use crate::tests::new_initialized_memory_db;
271
272 use super::super::tests::get_error_kind;
273 use super::super::tests::notsettable;
274 use super::test::{
275 make_info_collection, make_info_episode, make_info_movie, make_info_season, make_info_show,
276 };
277
278 #[tokio::test]
279 async fn use_test_macros() {
280 let db = new_initialized_memory_db().await;
281
282 make_info_collection!(&db, 1);
283 make_info_movie!(&db, 1);
284 make_info_show!(&db, 1);
285 make_info_season!(&db, 1, 1);
286 make_info_episode!(&db, 1, 1, 1);
287 }
288
289 #[tokio::test]
290 async fn test_round_trip_collections() {
291 let db = new_initialized_memory_db().await;
292
293 macro_rules! assert_collection {
294 ($db:expr, $id:literal, Success $(; $($skip:ident),+)?) => {
295 let model = assert_collection!(@insert, $db, $id $(; $($skip),+)?)
296 .expect("insert");
297
298 assert_eq!(model.id, CollectionId::from_raw($id));
299 assert_eq!(model.title, concat!("C Title ", $id));
300 assert_eq!(model.overview, concat!("C Overview ", $id));
301 };
302 ($db:expr, $id:literal, $error:ident $(; $($skip:ident),+)?) => {
303 let model = assert_collection!(@insert, $db, $id $(; $($skip),+)?)
304 .expect_err("insert");
305
306 assert_eq!(get_error_kind(model).expect("get_error_kind"), ErrorKind::$error);
307 };
308 (@insert, $db:expr, $id:literal $(; $($skip:ident),+)?) => {
309 super::collections::ActiveModel {
310 id: notsettable!(id, CollectionId::from_raw($id) $(, $($skip),+)?),
311 title: notsettable!(title, concat!("C Title ", $id).to_string() $(, $($skip),+)?),
312 overview: notsettable!(overview, concat!("C Overview ", $id).to_string() $(, $($skip),+)?),
313 }.insert($db).await
314 };
315 }
316
317 assert_collection!(&db, 1, Success);
318 assert_collection!(&db, 1, UniqueViolation);
319 assert_collection!(&db, 2, Success);
320
321 assert_collection!(&db, 3, Success; id);
322 assert_collection!(&db, 4, NotNullViolation; title);
323 assert_collection!(&db, 5, NotNullViolation; overview);
324 }
325
326 #[tokio::test]
327 async fn test_round_trip_movies() {
328 let db = new_initialized_memory_db().await;
329
330 macro_rules! assert_movie {
331 ($db:expr, $id:literal, Success $(; $($skip:ident),+)?) => {
332 let model = assert_movie!(@insert, $db, $id $(; $($skip),+)?)
333 .expect("insert");
334
335 assert_eq!(model.id, MovieId::from_raw($id));
336 assert_eq!(model.title, concat!("M Title ", $id));
337 assert_eq!(model.tagline, concat!("M Tagline ", $id));
338 assert_eq!(model.overview, concat!("M Overview ", $id));
339 assert_eq!(model.date, NaiveDate::from_yo_opt($id, 1).expect("from_yo_opt"));
340 };
341 ($db:expr, $id:literal, $error:ident $(; $($skip:ident),+)?) => {
342 let model = assert_movie!(@insert, $db, $id $(; $($skip),+)?)
343 .expect_err("insert");
344
345 assert_eq!(get_error_kind(model).expect("get_error_kind"), ErrorKind::$error);
346 };
347 (@insert, $db:expr, $id:literal $(; $($skip:ident),+)?) => {
348 super::movies::ActiveModel {
349 id: notsettable!(id, MovieId::from_raw($id) $(, $($skip),+)?),
350 title: notsettable!(title, concat!("M Title ", $id).to_string() $(, $($skip),+)?),
351 tagline: notsettable!(tagline, concat!("M Tagline ", $id).to_string() $(, $($skip),+)?),
352 overview: notsettable!(overview, concat!("M Overview ", $id).to_string() $(, $($skip),+)?),
353 date: notsettable!(date, NaiveDate::from_yo_opt($id, 1).expect("from_yo_opt") $(, $($skip),+)?),
354 }.insert($db).await
355 };
356 }
357
358 assert_movie!(&db, 1, Success);
359 assert_movie!(&db, 1, UniqueViolation);
360 assert_movie!(&db, 2, Success);
361
362 assert_movie!(&db, 3, Success; id);
363 assert_movie!(&db, 4, NotNullViolation; title);
364 assert_movie!(&db, 5, NotNullViolation; tagline);
365 assert_movie!(&db, 6, NotNullViolation; overview);
366 assert_movie!(&db, 7, NotNullViolation; date);
367 }
368
369 #[tokio::test]
370 async fn test_round_trip_shows() {
371 let db = new_initialized_memory_db().await;
372
373 macro_rules! assert_show {
374 ($db:expr, $id:literal, Success $(; $($skip:ident),+)?) => {
375 let model = assert_show!(@insert, $db, $id $(; $($skip),+)?)
376 .expect("insert");
377
378 assert_eq!(model.id, ShowId::from_raw($id));
379 assert_eq!(model.title, concat!("S Title ", $id));
380 assert_eq!(model.tagline, concat!("S Tagline ", $id));
381 assert_eq!(model.overview, concat!("S Overview ", $id));
382 assert_eq!(model.date, NaiveDate::from_yo_opt($id, 1).expect("from_yo_opt"));
383 };
384 ($db:expr, $id:literal, $error:ident $(; $($skip:ident),+)?) => {
385 let model = assert_show!(@insert, $db, $id $(; $($skip),+)?)
386 .expect_err("insert");
387
388 assert_eq!(
389 get_error_kind(model).expect("get_error_kind"),
390 ErrorKind::$error
391 );
392 };
393 (@insert, $db:expr, $id:literal $(; $($skip:ident),+)?) => {
394 super::shows::ActiveModel {
395 id: notsettable!(id, ShowId::from_raw($id) $(, $($skip),+)?),
396 title: notsettable!(title, concat!("S Title ", $id).to_string() $(, $($skip),+)?),
397 tagline: notsettable!(tagline, concat!("S Tagline ", $id).to_string() $(, $($skip),+)?),
398 overview: notsettable!(overview, concat!("S Overview ", $id).to_string() $(, $($skip),+)?),
399 date: notsettable!(date, NaiveDate::from_yo_opt($id, 1).expect("from_yo_opt") $(, $($skip),+)?),
400 }.insert($db).await
401 };
402 }
403
404 assert_show!(&db, 1, Success);
405 assert_show!(&db, 1, UniqueViolation);
406 assert_show!(&db, 2, Success);
407
408 assert_show!(&db, 3, Success; id);
409 assert_show!(&db, 4, NotNullViolation; title);
410 assert_show!(&db, 5, NotNullViolation; tagline);
411 assert_show!(&db, 6, NotNullViolation; overview);
412 assert_show!(&db, 7, NotNullViolation; date);
413 }
414
415 #[tokio::test]
416 async fn test_round_trip_seasons() {
417 let db = new_initialized_memory_db().await;
418
419 macro_rules! assert_season {
420 ($db:expr, $show:literal, $season:literal, Success $(; $($skip:ident),+)?) => {
421 let model = assert_season!(@insert, $db, $show, $season $(; $($skip),+)?)
422 .expect("insert");
423
424 assert_eq!(model.show_id, ShowId::from_raw($show));
425 assert_eq!(model.season_number, $season);
426 assert_eq!(model.title, concat!("SS Title ", $show, ",", $season));
427 assert_eq!(model.overview, concat!("SS Overview ", $show, ",", $season));
428 assert_eq!(model.date, NaiveDate::from_yo_opt($show + $season, 1).expect("from_yo_opt"));
429 };
430 ($db:expr, $show:literal, $season:literal, $error:ident $(; $($skip:ident),+)?) => {
431 let model = assert_season!(@insert, $db, $show, $season $(; $($skip),+)?)
432 .expect_err("insert");
433
434 assert_eq!(
435 get_error_kind(model).expect("get_error_kind"),
436 ErrorKind::$error
437 );
438 };
439 (@insert, $db:expr, $show:literal, $season:literal $(; $($skip:ident),+)?) => {
440 super::seasons::ActiveModel {
441 show_id: notsettable!(show_id, ShowId::from_raw($show) $(, $($skip),+)?),
442 season_number: notsettable!(season_number, $season $(, $($skip),+)?),
443 title: notsettable!(title, concat!("SS Title ", $show, ",", $season).to_string() $(, $($skip),+)?),
444 overview: notsettable!(overview, concat!("SS Overview ", $show, ",", $season).to_string() $(, $($skip),+)?),
445 date: notsettable!(date, NaiveDate::from_yo_opt($show + $season, 1).expect("from_yo_opt") $(, $($skip),+)?),
446 }.insert($db).await
447 };
448 }
449
450 assert_season!(&db, 1, 1, ForeignKeyViolation);
451 make_info_show!(&db, 1);
452 make_info_show!(&db, 2);
453
454 assert_season!(&db, 1, 1, Success);
455 assert_season!(&db, 1, 1, UniqueViolation);
456 assert_season!(&db, 2, 1, Success);
457 assert_season!(&db, 1, 2, Success);
458
459 assert_season!(&db, 1, 3, NotNullViolation; show_id);
460 assert_season!(&db, 1, 4, NotNullViolation; season_number);
461 assert_season!(&db, 1, 5, NotNullViolation; title);
462 assert_season!(&db, 1, 6, NotNullViolation; overview);
463 assert_season!(&db, 1, 7, NotNullViolation; date);
464 }
465
466 #[tokio::test]
467 async fn test_round_trip_episodes() {
468 let db = new_initialized_memory_db().await;
469
470 macro_rules! assert_episode {
471 ($db:expr, $show:literal, $season:literal, $episode:literal, Success $(; $($skip:ident),+)?) => {
472 let model = assert_episode!(@insert, $db, $show, $season, $episode $(; $($skip),+)?)
473 .expect("insert");
474
475 assert_eq!(model.show_id, ShowId::from_raw($show));
476 assert_eq!(model.season_number, $season);
477 assert_eq!(model.episode_number, $episode);
478 assert_eq!(model.title, concat!("SSE Title ", $show, ",", $season, ",", $episode));
479 assert_eq!(model.overview, concat!("SSE Overview ", $show, ",", $season, ",", $episode));
480 assert_eq!(model.date, NaiveDate::from_yo_opt($show + $season, 1).expect("from_yo_opt"));
481 };
482 ($db:expr, $show:literal, $season:literal, $episode:literal, $error:ident $(; $($skip:ident),+)?) => {
483 let model = assert_episode!(@insert, $db, $show, $season, $episode $(; $($skip),+)?)
484 .expect_err("insert");
485
486 assert_eq!(
487 get_error_kind(model).expect("get_error_kind"),
488 ErrorKind::$error
489 );
490 };
491 (@insert, $db:expr, $show:literal, $season:literal, $episode:literal $(; $($skip:ident),+)?) => {
492 super::episodes::ActiveModel {
493 show_id: notsettable!(show_id, ShowId::from_raw($show) $(, $($skip),+)?),
494 season_number: notsettable!(season_number, $season $(, $($skip),+)?),
495 episode_number: notsettable!(episode_number, $episode $(, $($skip),+)?),
496 title: notsettable!(title, concat!("SSE Title ", $show, ",", $season, ",", $episode).to_string() $(, $($skip),+)?),
497 overview: notsettable!(overview, concat!("SSE Overview ", $show, ",", $season, ",", $episode).to_string() $(, $($skip),+)?),
498 date: notsettable!(date, NaiveDate::from_yo_opt($show + $season, 1).expect("from_yo_opt") $(, $($skip),+)?),
499 }.insert($db).await
500 };
501 }
502
503 assert_episode!(&db, 1, 1, 1, ForeignKeyViolation);
504 make_info_show!(&db, 1);
505 make_info_show!(&db, 2);
506 assert_episode!(&db, 1, 1, 1, ForeignKeyViolation);
507 make_info_season!(&db, 1, 1);
508 make_info_season!(&db, 1, 2);
509 make_info_season!(&db, 2, 1);
510
511 assert_episode!(&db, 1, 1, 1, Success);
512 assert_episode!(&db, 1, 1, 1, UniqueViolation);
513 assert_episode!(&db, 2, 1, 1, Success);
514 assert_episode!(&db, 1, 2, 1, Success);
515 assert_episode!(&db, 1, 1, 2, Success);
516
517 assert_episode!(&db, 1, 1, 3, NotNullViolation; show_id);
518 assert_episode!(&db, 1, 1, 4, NotNullViolation; season_number);
519 assert_episode!(&db, 1, 1, 4, NotNullViolation; episode_number);
520 assert_episode!(&db, 1, 1, 5, NotNullViolation; title);
521 assert_episode!(&db, 1, 1, 6, NotNullViolation; overview);
522 assert_episode!(&db, 1, 1, 7, NotNullViolation; date);
523 }
524}