You've already forked flix
Update dependencies and lints
This commit is contained in:
@@ -1,14 +1,16 @@
|
||||
[package]
|
||||
name = "flix-db"
|
||||
version = "0.0.20"
|
||||
license-file.workspace = true
|
||||
|
||||
version = "0.1.0"
|
||||
description = "Types for storing persistent data about media"
|
||||
repository = "https://github.com/QuantumShade/flix"
|
||||
categories = []
|
||||
repository = "https://git.skrundz.dev/quantumshade/flix"
|
||||
keywords = ["flix"]
|
||||
categories = ["multimedia"]
|
||||
include = ["/src"]
|
||||
publish = true
|
||||
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
license-file.workspace = true
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
@@ -29,7 +31,7 @@ flix-tmdb = { workspace = true, features = ["sea-orm"], optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
sea-orm-migration = { workspace = true, features = ["runtime-tokio-rustls"] }
|
||||
tokio = { version = "^1", default-features = false, features = [
|
||||
tokio = { workspace = true, features = [
|
||||
"macros",
|
||||
"rt",
|
||||
] }
|
||||
|
||||
@@ -1,15 +1,26 @@
|
||||
//! Types and functions related to [DatabaseConnection]s
|
||||
//! Types and functions related to [`DatabaseConnection`]s.
|
||||
|
||||
use sea_orm::{DatabaseConnection, DbErr};
|
||||
use sea_orm_migration::MigratorTrait as _;
|
||||
|
||||
/// A newtype wrapping a [DatabaseConnection]
|
||||
/// A newtype wrapping a [`DatabaseConnection`].
|
||||
#[derive(Debug)]
|
||||
pub struct Connection(DatabaseConnection);
|
||||
|
||||
impl Connection {
|
||||
/// Helper function for applying database migrations while wrapping a
|
||||
/// [DatabaseConnection] in a newtype
|
||||
/// [`DatabaseConnection`] in a newtype.
|
||||
///
|
||||
/// # Errors
|
||||
/// Fails if connecting or applying migrations fail.
|
||||
#[inline]
|
||||
pub async fn try_from(db: DatabaseConnection) -> Result<Self, DbErr> {
|
||||
// The migrations only create views which store no data, so it is
|
||||
// important to down before up since modifications are impossible.
|
||||
//
|
||||
// Syncing the schema registry allows all real tables to be upgraded.
|
||||
// It is important to sync twice to ensure internal consistency so that
|
||||
// the views can be recreated on the latest schema.
|
||||
crate::migration::Migrator::down(&db, None).await?;
|
||||
db.get_schema_registry("flix_db::*").sync(&db).await?;
|
||||
db.get_schema_registry("flix_db::*").sync(&db).await?;
|
||||
@@ -19,6 +30,7 @@ impl Connection {
|
||||
}
|
||||
|
||||
impl AsRef<DatabaseConnection> for Connection {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &DatabaseConnection {
|
||||
&self.0
|
||||
}
|
||||
|
||||
+183
-145
@@ -1,6 +1,6 @@
|
||||
//! This module contains entities for storing media file information
|
||||
//! This module contains entities for storing media file information.
|
||||
|
||||
/// Library entity
|
||||
/// Library entity.
|
||||
pub mod libraries {
|
||||
use flix_model::id::LibraryId;
|
||||
|
||||
@@ -10,42 +10,43 @@ pub mod libraries {
|
||||
use chrono::{DateTime, Utc};
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
/// The database representation of a library media folder
|
||||
/// The database representation of a library media folder.
|
||||
#[sea_orm::model]
|
||||
#[derive(Debug, Clone, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "flix_libraries")]
|
||||
pub struct Model {
|
||||
/// The library's ID
|
||||
/// The library's ID.
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: LibraryId,
|
||||
/// The library's directory
|
||||
/// The library's directory.
|
||||
pub directory: PathBytes,
|
||||
/// The library's last scan data
|
||||
/// The library's last scan data.
|
||||
pub last_scan_date: Option<DateTime<Utc>>,
|
||||
/// The library's last scan duration
|
||||
/// The library's last scan duration.
|
||||
pub last_scan_duration: Option<Seconds>,
|
||||
|
||||
/// Collections that are part of this library
|
||||
/// Collections that are part of this library.
|
||||
#[sea_orm(has_many)]
|
||||
pub collections: HasMany<super::collections::Entity>,
|
||||
/// Movies that are part of this library
|
||||
/// Movies that are part of this library.
|
||||
#[sea_orm(has_many)]
|
||||
pub movies: HasMany<super::movies::Entity>,
|
||||
/// Shows that are part of this library
|
||||
/// Shows that are part of this library.
|
||||
#[sea_orm(has_many)]
|
||||
pub shows: HasMany<super::shows::Entity>,
|
||||
/// Seasons that are part of this library
|
||||
/// Seasons that are part of this library.
|
||||
#[sea_orm(has_many)]
|
||||
pub seasons: HasMany<super::seasons::Entity>,
|
||||
/// Episodes that are part of this library
|
||||
/// Episodes that are part of this library.
|
||||
#[sea_orm(has_many)]
|
||||
pub episodes: HasMany<super::episodes::Entity>,
|
||||
}
|
||||
|
||||
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
}
|
||||
|
||||
/// Collection entity
|
||||
/// Collection entity.
|
||||
pub mod collections {
|
||||
use flix_model::id::{CollectionId, LibraryId};
|
||||
|
||||
@@ -55,25 +56,25 @@ pub mod collections {
|
||||
|
||||
use crate::entity;
|
||||
|
||||
/// The database representation of a collection media folder
|
||||
/// The database representation of a collection media folder.
|
||||
#[sea_orm::model]
|
||||
#[derive(Debug, Clone, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "flix_collections")]
|
||||
pub struct Model {
|
||||
/// The collection's ID
|
||||
/// The collection's ID.
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: CollectionId,
|
||||
/// The collection's parent
|
||||
/// The collection's parent.
|
||||
#[sea_orm(indexed)]
|
||||
pub parent_id: Option<CollectionId>,
|
||||
/// The collection's library ID
|
||||
/// The collection's library ID.
|
||||
pub library_id: LibraryId,
|
||||
/// The collection's directory
|
||||
/// The collection's directory.
|
||||
pub directory: PathBytes,
|
||||
/// The collection's poster path
|
||||
/// The collection's poster path.
|
||||
pub relative_poster_path: Option<String>,
|
||||
|
||||
/// This collection's parent
|
||||
/// This collection's parent.
|
||||
#[sea_orm(
|
||||
self_ref,
|
||||
relation_enum = "Parent",
|
||||
@@ -83,7 +84,7 @@ pub mod collections {
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
pub parent: HasOne<Entity>,
|
||||
/// The library this collection belongs to
|
||||
/// The library this collection belongs to.
|
||||
#[sea_orm(
|
||||
belongs_to,
|
||||
from = "library_id",
|
||||
@@ -92,7 +93,7 @@ pub mod collections {
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
pub library: HasOne<super::libraries::Entity>,
|
||||
/// The info for this collection
|
||||
/// The info for this collection.
|
||||
#[sea_orm(
|
||||
belongs_to,
|
||||
relation_enum = "Info",
|
||||
@@ -103,15 +104,16 @@ pub mod collections {
|
||||
)]
|
||||
pub info: HasOne<entity::info::collections::Entity>,
|
||||
|
||||
/// The watched info for this collection
|
||||
/// The watched info for this collection.
|
||||
#[sea_orm(has_many, relation_enum = "Watched", from = "id", to = "id")]
|
||||
pub watched: HasMany<entity::watched::collections::Entity>,
|
||||
}
|
||||
|
||||
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
}
|
||||
|
||||
/// Movie entity
|
||||
/// Movie entity.
|
||||
pub mod movies {
|
||||
use flix_model::id::{CollectionId, LibraryId, MovieId};
|
||||
|
||||
@@ -121,27 +123,27 @@ pub mod movies {
|
||||
|
||||
use crate::entity;
|
||||
|
||||
/// The database representation of a movie media folder
|
||||
/// The database representation of a movie media folder.
|
||||
#[sea_orm::model]
|
||||
#[derive(Debug, Clone, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "flix_movies")]
|
||||
pub struct Model {
|
||||
/// The movie's ID
|
||||
/// The movie's ID.
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: MovieId,
|
||||
/// The movie's parent
|
||||
/// The movie's parent.
|
||||
#[sea_orm(indexed)]
|
||||
pub parent_id: Option<CollectionId>,
|
||||
/// The movie's library
|
||||
/// The movie's library.
|
||||
pub library_id: LibraryId,
|
||||
/// The movie's directory
|
||||
/// The movie's directory.
|
||||
pub directory: PathBytes,
|
||||
/// The movie's media path
|
||||
/// The movie's media path.
|
||||
pub relative_media_path: String,
|
||||
/// The movie's poster path
|
||||
/// The movie's poster path.
|
||||
pub relative_poster_path: Option<String>,
|
||||
|
||||
/// This movie's parent
|
||||
/// This movie's parent.
|
||||
#[sea_orm(
|
||||
belongs_to,
|
||||
from = "parent_id",
|
||||
@@ -150,7 +152,7 @@ pub mod movies {
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
pub parent: HasOne<super::collections::Entity>,
|
||||
/// The library this movie belongs to
|
||||
/// The library this movie belongs to.
|
||||
#[sea_orm(
|
||||
belongs_to,
|
||||
from = "library_id",
|
||||
@@ -159,7 +161,7 @@ pub mod movies {
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
pub library: HasOne<super::libraries::Entity>,
|
||||
/// The info for this movie
|
||||
/// The info for this movie.
|
||||
#[sea_orm(
|
||||
belongs_to,
|
||||
relation_enum = "Info",
|
||||
@@ -170,15 +172,16 @@ pub mod movies {
|
||||
)]
|
||||
pub info: HasOne<entity::info::movies::Entity>,
|
||||
|
||||
/// The watched info for this movie
|
||||
/// The watched info for this movie.
|
||||
#[sea_orm(has_many, relation_enum = "Watched", from = "id", to = "id")]
|
||||
pub watched: HasMany<entity::watched::movies::Entity>,
|
||||
}
|
||||
|
||||
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
}
|
||||
|
||||
/// Show entity
|
||||
/// Show entity.
|
||||
pub mod shows {
|
||||
use flix_model::id::{CollectionId, LibraryId, ShowId};
|
||||
|
||||
@@ -188,25 +191,25 @@ pub mod shows {
|
||||
|
||||
use crate::entity;
|
||||
|
||||
/// The database representation of a show media folder
|
||||
/// The database representation of a show media folder.
|
||||
#[sea_orm::model]
|
||||
#[derive(Debug, Clone, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "flix_shows")]
|
||||
pub struct Model {
|
||||
/// The show's ID
|
||||
/// The show's ID.
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: ShowId,
|
||||
/// The show's parent
|
||||
/// The show's parent.
|
||||
#[sea_orm(indexed)]
|
||||
pub parent_id: Option<CollectionId>,
|
||||
/// The show's library
|
||||
/// The show's library.
|
||||
pub library_id: LibraryId,
|
||||
/// The show's directory
|
||||
/// The show's directory.
|
||||
pub directory: PathBytes,
|
||||
/// The show's poster path
|
||||
/// The show's poster path.
|
||||
pub relative_poster_path: Option<String>,
|
||||
|
||||
/// This show's parent
|
||||
/// This show's parent.
|
||||
#[sea_orm(
|
||||
belongs_to,
|
||||
from = "parent_id",
|
||||
@@ -215,7 +218,7 @@ pub mod shows {
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
pub parent: HasOne<super::collections::Entity>,
|
||||
/// The library this show belongs to
|
||||
/// The library this show belongs to.
|
||||
#[sea_orm(
|
||||
belongs_to,
|
||||
from = "library_id",
|
||||
@@ -224,7 +227,7 @@ pub mod shows {
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
pub library: HasOne<super::libraries::Entity>,
|
||||
/// The info for this show
|
||||
/// The info for this show.
|
||||
#[sea_orm(
|
||||
belongs_to,
|
||||
relation_enum = "Info",
|
||||
@@ -235,21 +238,22 @@ pub mod shows {
|
||||
)]
|
||||
pub info: HasOne<entity::info::shows::Entity>,
|
||||
|
||||
/// Seasons that are part of this show
|
||||
/// Seasons that are part of this show.
|
||||
#[sea_orm(has_many)]
|
||||
pub seasons: HasMany<super::seasons::Entity>,
|
||||
/// Episodes that are part of this show
|
||||
/// Episodes that are part of this show.
|
||||
#[sea_orm(has_many)]
|
||||
pub episodes: HasMany<super::episodes::Entity>,
|
||||
/// The watched info for this show
|
||||
/// The watched info for this show.
|
||||
#[sea_orm(has_many, relation_enum = "Watched", from = "id", to = "id")]
|
||||
pub watched: HasMany<entity::watched::shows::Entity>,
|
||||
}
|
||||
|
||||
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
}
|
||||
|
||||
/// Season entity
|
||||
/// Season entity.
|
||||
pub mod seasons {
|
||||
use flix_model::id::{LibraryId, ShowId};
|
||||
use flix_model::numbers::SeasonNumber;
|
||||
@@ -260,25 +264,25 @@ pub mod seasons {
|
||||
|
||||
use crate::entity;
|
||||
|
||||
/// The database representation of a season media folder
|
||||
/// The database representation of a season media folder.
|
||||
#[sea_orm::model]
|
||||
#[derive(Debug, Clone, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "flix_seasons")]
|
||||
pub struct Model {
|
||||
/// The season's show's ID
|
||||
/// The season's show's ID.
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub show_id: ShowId,
|
||||
/// The season's number
|
||||
/// The season's number.
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub season_number: SeasonNumber,
|
||||
/// The season's library
|
||||
/// The season's library.
|
||||
pub library_id: LibraryId,
|
||||
/// The season's directory
|
||||
/// The season's directory.
|
||||
pub directory: PathBytes,
|
||||
/// The season's poster path
|
||||
/// The season's poster path.
|
||||
pub relative_poster_path: Option<String>,
|
||||
|
||||
/// This season's show
|
||||
/// This season's show.
|
||||
#[sea_orm(
|
||||
belongs_to,
|
||||
from = "show_id",
|
||||
@@ -287,7 +291,7 @@ pub mod seasons {
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
pub show: HasOne<super::shows::Entity>,
|
||||
/// The library this season belongs to
|
||||
/// The library this season belongs to.
|
||||
#[sea_orm(
|
||||
belongs_to,
|
||||
from = "library_id",
|
||||
@@ -296,7 +300,7 @@ pub mod seasons {
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
pub library: HasOne<super::libraries::Entity>,
|
||||
/// The info for this season
|
||||
/// The info for this season.
|
||||
#[sea_orm(
|
||||
belongs_to,
|
||||
relation_enum = "Info",
|
||||
@@ -307,10 +311,10 @@ pub mod seasons {
|
||||
)]
|
||||
pub info: HasOne<entity::info::seasons::Entity>,
|
||||
|
||||
/// Episodes that are part of this show
|
||||
/// Episodes that are part of this show.
|
||||
#[sea_orm(has_many)]
|
||||
pub episodes: HasMany<super::episodes::Entity>,
|
||||
/// The watched info for this season
|
||||
/// The watched info for this season.
|
||||
#[sea_orm(
|
||||
has_many,
|
||||
relation_enum = "Watched",
|
||||
@@ -320,10 +324,11 @@ pub mod seasons {
|
||||
pub watched: HasMany<entity::watched::seasons::Entity>,
|
||||
}
|
||||
|
||||
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
}
|
||||
|
||||
/// Episode entity
|
||||
/// Episode entity.
|
||||
pub mod episodes {
|
||||
use flix_model::id::{LibraryId, ShowId};
|
||||
use flix_model::numbers::{EpisodeNumber, SeasonNumber};
|
||||
@@ -334,32 +339,32 @@ pub mod episodes {
|
||||
|
||||
use crate::entity;
|
||||
|
||||
/// The database representation of a episode media folder
|
||||
/// The database representation of a episode media folder.
|
||||
#[sea_orm::model]
|
||||
#[derive(Debug, Clone, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "flix_episodes")]
|
||||
pub struct Model {
|
||||
/// The episode's show's ID
|
||||
/// The episode's show's ID.
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub show_id: ShowId,
|
||||
/// The episode's season's number
|
||||
/// The episode's season's number.
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub season_number: SeasonNumber,
|
||||
/// The episode's number
|
||||
/// The episode's number.
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub episode_number: EpisodeNumber,
|
||||
/// The number of additional contained episodes
|
||||
/// The number of additional contained episodes.
|
||||
pub count: u8,
|
||||
/// The episode's library
|
||||
/// The episode's library.
|
||||
pub library_id: LibraryId,
|
||||
/// The episode's directory
|
||||
/// The episode's directory.
|
||||
pub directory: PathBytes,
|
||||
/// The episode's media path
|
||||
/// The episode's media path.
|
||||
pub relative_media_path: String,
|
||||
/// The episode's poster path
|
||||
/// The episode's poster path.
|
||||
pub relative_poster_path: Option<String>,
|
||||
|
||||
/// This episode's show
|
||||
/// This episode's show.
|
||||
#[sea_orm(
|
||||
belongs_to,
|
||||
from = "show_id",
|
||||
@@ -368,7 +373,7 @@ pub mod episodes {
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
pub show: HasOne<super::shows::Entity>,
|
||||
/// This episode's season
|
||||
/// This episode's season.
|
||||
#[sea_orm(
|
||||
belongs_to,
|
||||
from = "(show_id, season_number)",
|
||||
@@ -377,7 +382,7 @@ pub mod episodes {
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
pub season: HasOne<super::seasons::Entity>,
|
||||
/// The library this episode belongs to
|
||||
/// The library this episode belongs to.
|
||||
#[sea_orm(
|
||||
belongs_to,
|
||||
from = "library_id",
|
||||
@@ -386,7 +391,7 @@ pub mod episodes {
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
pub library: HasOne<super::libraries::Entity>,
|
||||
/// The info for this episode
|
||||
/// The info for this episode.
|
||||
#[sea_orm(
|
||||
belongs_to,
|
||||
relation_enum = "Info",
|
||||
@@ -397,7 +402,7 @@ pub mod episodes {
|
||||
)]
|
||||
pub info: HasOne<entity::info::episodes::Entity>,
|
||||
|
||||
/// The watched info for this episode
|
||||
/// The watched info for this episode.
|
||||
#[sea_orm(
|
||||
has_many,
|
||||
relation_enum = "Watched",
|
||||
@@ -407,23 +412,26 @@ pub mod episodes {
|
||||
pub watched: HasMany<entity::watched::episodes::Entity>,
|
||||
}
|
||||
|
||||
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
}
|
||||
|
||||
/// Macros for creating content entities
|
||||
/// Macros for creating content entities.
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
macro_rules! make_content_library {
|
||||
($db:expr, $id:expr) => {
|
||||
$crate::entity::content::libraries::ActiveModel {
|
||||
id: Set(::flix_model::id::LibraryId::from_raw($id)),
|
||||
directory: Set(::std::path::PathBuf::new().into()),
|
||||
last_scan_date: Set(None),
|
||||
last_scan_duration: Set(None),
|
||||
}
|
||||
.insert($db)
|
||||
.await
|
||||
.expect("insert");
|
||||
drop(
|
||||
$crate::entity::content::libraries::ActiveModel {
|
||||
id: Set(::flix_model::id::LibraryId::from_raw($id)),
|
||||
directory: Set(::std::path::PathBuf::new().into()),
|
||||
last_scan_date: Set(None),
|
||||
last_scan_duration: Set(None),
|
||||
}
|
||||
.insert($db)
|
||||
.await
|
||||
.expect("insert"),
|
||||
);
|
||||
};
|
||||
}
|
||||
pub(crate) use make_content_library;
|
||||
@@ -431,16 +439,18 @@ pub mod test {
|
||||
macro_rules! make_content_collection {
|
||||
($db:expr, $lid:expr, $id:expr, $pid:expr) => {
|
||||
$crate::entity::info::test::make_info_collection!($db, $id);
|
||||
$crate::entity::content::collections::ActiveModel {
|
||||
id: Set(::flix_model::id::CollectionId::from_raw($id)),
|
||||
parent_id: Set($pid.map(::flix_model::id::CollectionId::from_raw)),
|
||||
library_id: Set(::flix_model::id::LibraryId::from_raw($lid)),
|
||||
directory: Set(::std::path::PathBuf::new().into()),
|
||||
relative_poster_path: Set(::core::option::Option::None),
|
||||
}
|
||||
.insert($db)
|
||||
.await
|
||||
.expect("insert");
|
||||
drop(
|
||||
$crate::entity::content::collections::ActiveModel {
|
||||
id: Set(::flix_model::id::CollectionId::from_raw($id)),
|
||||
parent_id: Set($pid.map(::flix_model::id::CollectionId::from_raw)),
|
||||
library_id: Set(::flix_model::id::LibraryId::from_raw($lid)),
|
||||
directory: Set(::std::path::PathBuf::new().into()),
|
||||
relative_poster_path: Set(::core::option::Option::None),
|
||||
}
|
||||
.insert($db)
|
||||
.await
|
||||
.expect("insert"),
|
||||
);
|
||||
};
|
||||
}
|
||||
pub(crate) use make_content_collection;
|
||||
@@ -448,17 +458,19 @@ pub mod test {
|
||||
macro_rules! make_content_movie {
|
||||
($db:expr, $lid:expr, $id:expr, $pid:expr) => {
|
||||
$crate::entity::info::test::make_info_movie!($db, $id);
|
||||
$crate::entity::content::movies::ActiveModel {
|
||||
id: Set(::flix_model::id::MovieId::from_raw($id)),
|
||||
parent_id: Set($pid.map(::flix_model::id::CollectionId::from_raw)),
|
||||
library_id: Set(::flix_model::id::LibraryId::from_raw($lid)),
|
||||
directory: Set(::std::path::PathBuf::new().into()),
|
||||
relative_media_path: Set(::std::string::String::new()),
|
||||
relative_poster_path: Set(::core::option::Option::None),
|
||||
}
|
||||
.insert($db)
|
||||
.await
|
||||
.expect("insert");
|
||||
drop(
|
||||
$crate::entity::content::movies::ActiveModel {
|
||||
id: Set(::flix_model::id::MovieId::from_raw($id)),
|
||||
parent_id: Set($pid.map(::flix_model::id::CollectionId::from_raw)),
|
||||
library_id: Set(::flix_model::id::LibraryId::from_raw($lid)),
|
||||
directory: Set(::std::path::PathBuf::new().into()),
|
||||
relative_media_path: Set(::alloc::string::String::new()),
|
||||
relative_poster_path: Set(::core::option::Option::None),
|
||||
}
|
||||
.insert($db)
|
||||
.await
|
||||
.expect("insert"),
|
||||
);
|
||||
};
|
||||
}
|
||||
pub(crate) use make_content_movie;
|
||||
@@ -466,16 +478,18 @@ pub mod test {
|
||||
macro_rules! make_content_show {
|
||||
($db:expr, $lid:expr, $id:expr, $pid:expr) => {
|
||||
$crate::entity::info::test::make_info_show!($db, $id);
|
||||
$crate::entity::content::shows::ActiveModel {
|
||||
id: Set(::flix_model::id::ShowId::from_raw($id)),
|
||||
parent_id: Set($pid.map(::flix_model::id::CollectionId::from_raw)),
|
||||
library_id: Set(::flix_model::id::LibraryId::from_raw($lid)),
|
||||
directory: Set(::std::path::PathBuf::new().into()),
|
||||
relative_poster_path: Set(::core::option::Option::None),
|
||||
}
|
||||
.insert($db)
|
||||
.await
|
||||
.expect("insert");
|
||||
drop(
|
||||
$crate::entity::content::shows::ActiveModel {
|
||||
id: Set(::flix_model::id::ShowId::from_raw($id)),
|
||||
parent_id: Set($pid.map(::flix_model::id::CollectionId::from_raw)),
|
||||
library_id: Set(::flix_model::id::LibraryId::from_raw($lid)),
|
||||
directory: Set(::std::path::PathBuf::new().into()),
|
||||
relative_poster_path: Set(::core::option::Option::None),
|
||||
}
|
||||
.insert($db)
|
||||
.await
|
||||
.expect("insert"),
|
||||
);
|
||||
};
|
||||
}
|
||||
pub(crate) use make_content_show;
|
||||
@@ -483,16 +497,18 @@ pub mod test {
|
||||
macro_rules! make_content_season {
|
||||
($db:expr, $lid:expr, $show:expr, $season:expr) => {
|
||||
$crate::entity::info::test::make_info_season!($db, $show, $season);
|
||||
$crate::entity::content::seasons::ActiveModel {
|
||||
show_id: Set(::flix_model::id::ShowId::from_raw($show)),
|
||||
season_number: Set(::flix_model::numbers::SeasonNumber::new($season)),
|
||||
library_id: Set(::flix_model::id::LibraryId::from_raw($lid)),
|
||||
directory: Set(::std::path::PathBuf::new().into()),
|
||||
relative_poster_path: Set(::core::option::Option::None),
|
||||
}
|
||||
.insert($db)
|
||||
.await
|
||||
.expect("insert");
|
||||
drop(
|
||||
$crate::entity::content::seasons::ActiveModel {
|
||||
show_id: Set(::flix_model::id::ShowId::from_raw($show)),
|
||||
season_number: Set(::flix_model::numbers::SeasonNumber::new($season)),
|
||||
library_id: Set(::flix_model::id::LibraryId::from_raw($lid)),
|
||||
directory: Set(::std::path::PathBuf::new().into()),
|
||||
relative_poster_path: Set(::core::option::Option::None),
|
||||
}
|
||||
.insert($db)
|
||||
.await
|
||||
.expect("insert"),
|
||||
);
|
||||
};
|
||||
}
|
||||
pub(crate) use make_content_season;
|
||||
@@ -506,19 +522,21 @@ pub mod test {
|
||||
};
|
||||
(@make, $db:expr, $lid:expr, $show:expr, $season:expr, $episode:expr, $count:literal) => {
|
||||
$crate::entity::info::test::make_info_episode!($db, $show, $season, $episode);
|
||||
$crate::entity::content::episodes::ActiveModel {
|
||||
show_id: Set(::flix_model::id::ShowId::from_raw($show)),
|
||||
season_number: Set(::flix_model::numbers::SeasonNumber::new($season)),
|
||||
episode_number: Set(::flix_model::numbers::EpisodeNumber::new($episode)),
|
||||
count: Set($count),
|
||||
library_id: Set(::flix_model::id::LibraryId::from_raw($lid)),
|
||||
directory: Set(::std::path::PathBuf::new().into()),
|
||||
relative_media_path: Set(::std::string::String::new()),
|
||||
relative_poster_path: Set(::core::option::Option::None),
|
||||
}
|
||||
.insert($db)
|
||||
.await
|
||||
.expect("insert");
|
||||
drop(
|
||||
$crate::entity::content::episodes::ActiveModel {
|
||||
show_id: Set(::flix_model::id::ShowId::from_raw($show)),
|
||||
season_number: Set(::flix_model::numbers::SeasonNumber::new($season)),
|
||||
episode_number: Set(::flix_model::numbers::EpisodeNumber::new($episode)),
|
||||
count: Set($count),
|
||||
library_id: Set(::flix_model::id::LibraryId::from_raw($lid)),
|
||||
directory: Set(::std::path::PathBuf::new().into()),
|
||||
relative_media_path: Set(::alloc::string::String::new()),
|
||||
relative_poster_path: Set(::core::option::Option::None),
|
||||
}
|
||||
.insert($db)
|
||||
.await
|
||||
.expect("insert"),
|
||||
);
|
||||
};
|
||||
}
|
||||
pub(crate) use make_content_episode;
|
||||
@@ -550,7 +568,10 @@ mod tests {
|
||||
use super::super::tests::get_error_kind;
|
||||
use super::super::tests::{noneable, notsettable};
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[tokio::test]
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
#[expect(clippy::default_numeric_fallback, reason = "unit test")]
|
||||
async fn use_test_macros() {
|
||||
let db = new_initialized_memory_db().await;
|
||||
|
||||
@@ -562,8 +583,10 @@ mod tests {
|
||||
make_content_episode!(&db, 1, 1, 1, 1);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[tokio::test]
|
||||
async fn test_round_trip_libraries() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
async fn round_trip_libraries() {
|
||||
let db = new_initialized_memory_db().await;
|
||||
|
||||
macro_rules! assert_library {
|
||||
@@ -601,8 +624,11 @@ mod tests {
|
||||
assert_library!(&db, 6, Success; last_scan_duration);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[tokio::test]
|
||||
async fn test_round_trip_collections() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
#[expect(clippy::default_numeric_fallback, reason = "unit test")]
|
||||
async fn round_trip_collections() {
|
||||
let db = new_initialized_memory_db().await;
|
||||
|
||||
macro_rules! assert_collection {
|
||||
@@ -656,8 +682,11 @@ mod tests {
|
||||
assert_collection!(&db, 7, None, 1, Success; relative_poster_path);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[tokio::test]
|
||||
async fn test_round_trip_movies() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
#[expect(clippy::default_numeric_fallback, reason = "unit test")]
|
||||
async fn round_trip_movies() {
|
||||
let db = new_initialized_memory_db().await;
|
||||
|
||||
macro_rules! assert_movie {
|
||||
@@ -718,8 +747,11 @@ mod tests {
|
||||
assert_movie!(&db, 8, None, 1, Success; relative_poster_path);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[tokio::test]
|
||||
async fn test_round_trip_shows() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
#[expect(clippy::default_numeric_fallback, reason = "unit test")]
|
||||
async fn round_trip_shows() {
|
||||
let db = new_initialized_memory_db().await;
|
||||
|
||||
macro_rules! assert_show {
|
||||
@@ -776,8 +808,11 @@ mod tests {
|
||||
assert_show!(&db, 7, None, 1, Success; relative_poster_path);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[tokio::test]
|
||||
async fn test_round_trip_seasons() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
#[expect(clippy::default_numeric_fallback, reason = "unit test")]
|
||||
async fn round_trip_seasons() {
|
||||
let db = new_initialized_memory_db().await;
|
||||
|
||||
macro_rules! assert_season {
|
||||
@@ -828,8 +863,11 @@ mod tests {
|
||||
assert_season!(&db, 1, 7, 1, Success; relative_poster_path);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[tokio::test]
|
||||
async fn test_round_trip_episodes() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
#[expect(clippy::default_numeric_fallback, reason = "unit test")]
|
||||
async fn round_trip_episodes() {
|
||||
let db = new_initialized_memory_db().await;
|
||||
|
||||
macro_rules! assert_episode {
|
||||
|
||||
+149
-119
@@ -1,7 +1,7 @@
|
||||
//! This module contains entities for storing media information such as
|
||||
//! titles and overviews
|
||||
//! titles and overviews.
|
||||
|
||||
/// Collection entity
|
||||
/// Collection entity.
|
||||
pub mod collections {
|
||||
use flix_model::id::CollectionId;
|
||||
|
||||
@@ -9,38 +9,39 @@ pub mod collections {
|
||||
|
||||
use crate::entity;
|
||||
|
||||
/// The database representation of a flix collection
|
||||
/// The database representation of a flix collection.
|
||||
#[sea_orm::model]
|
||||
#[derive(Debug, Clone, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "flix_info_collections")]
|
||||
pub struct Model {
|
||||
/// The collection's ID
|
||||
/// The collection's ID.
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: CollectionId,
|
||||
/// The collection's title
|
||||
/// The collection's title.
|
||||
pub title: String,
|
||||
/// The collection's overview
|
||||
/// The collection's overview.
|
||||
pub overview: String,
|
||||
|
||||
/// The sortable title
|
||||
/// The sortable title.
|
||||
#[sea_orm(indexed)]
|
||||
pub sort_title: String,
|
||||
/// The filesystem-safe slug
|
||||
/// The filesystem-safe slug.
|
||||
#[sea_orm(indexed, unique)]
|
||||
pub fs_slug: String,
|
||||
/// The url-safe slug
|
||||
/// The url-safe slug.
|
||||
#[sea_orm(indexed, unique)]
|
||||
pub web_slug: String,
|
||||
|
||||
/// Potential content for this collection
|
||||
/// Potential content for this collection.
|
||||
#[sea_orm(has_one)]
|
||||
pub content: HasOne<entity::content::collections::Entity>,
|
||||
}
|
||||
|
||||
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
}
|
||||
|
||||
/// Movie entity
|
||||
/// Movie entity.
|
||||
pub mod movies {
|
||||
use flix_model::id::MovieId;
|
||||
|
||||
@@ -49,43 +50,44 @@ pub mod movies {
|
||||
|
||||
use crate::entity;
|
||||
|
||||
/// The database representation of a flix movie
|
||||
/// The database representation of a flix movie.
|
||||
#[sea_orm::model]
|
||||
#[derive(Debug, Clone, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "flix_info_movies")]
|
||||
pub struct Model {
|
||||
/// The movie's ID
|
||||
/// The movie's ID.
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: MovieId,
|
||||
/// The movie's title
|
||||
/// The movie's title.
|
||||
pub title: String,
|
||||
/// The movie's tagline
|
||||
/// The movie's tagline.
|
||||
pub tagline: String,
|
||||
/// The movie's overview
|
||||
/// The movie's overview.
|
||||
pub overview: String,
|
||||
/// The movie's release date
|
||||
/// The movie's release date.
|
||||
#[sea_orm(indexed)]
|
||||
pub date: NaiveDate,
|
||||
|
||||
/// The sortable title
|
||||
/// The sortable title.
|
||||
#[sea_orm(indexed)]
|
||||
pub sort_title: String,
|
||||
/// The filesystem-safe slug
|
||||
/// The filesystem-safe slug.
|
||||
#[sea_orm(indexed, unique)]
|
||||
pub fs_slug: String,
|
||||
/// The url-safe slug
|
||||
/// The url-safe slug.
|
||||
#[sea_orm(indexed, unique)]
|
||||
pub web_slug: String,
|
||||
|
||||
/// Potential content for this movie
|
||||
/// Potential content for this movie.
|
||||
#[sea_orm(has_one)]
|
||||
pub content: HasOne<entity::content::movies::Entity>,
|
||||
}
|
||||
|
||||
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
}
|
||||
|
||||
/// Show entity
|
||||
/// Show entity.
|
||||
pub mod shows {
|
||||
use flix_model::id::ShowId;
|
||||
|
||||
@@ -94,50 +96,51 @@ pub mod shows {
|
||||
|
||||
use crate::entity;
|
||||
|
||||
/// The database representation of a flix show
|
||||
/// The database representation of a flix show.
|
||||
#[sea_orm::model]
|
||||
#[derive(Debug, Clone, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "flix_info_shows")]
|
||||
pub struct Model {
|
||||
/// The show's ID
|
||||
/// The show's ID.
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: ShowId,
|
||||
/// The show's title
|
||||
/// The show's title.
|
||||
pub title: String,
|
||||
/// The show's tagline
|
||||
/// The show's tagline.
|
||||
pub tagline: String,
|
||||
/// The show's overview
|
||||
/// The show's overview.
|
||||
pub overview: String,
|
||||
/// The show's air date
|
||||
/// The show's air date.
|
||||
#[sea_orm(indexed)]
|
||||
pub date: NaiveDate,
|
||||
|
||||
/// The sortable title
|
||||
/// The sortable title.
|
||||
#[sea_orm(indexed)]
|
||||
pub sort_title: String,
|
||||
/// The filesystem-safe slug
|
||||
/// The filesystem-safe slug.
|
||||
#[sea_orm(indexed, unique)]
|
||||
pub fs_slug: String,
|
||||
/// The url-safe slug
|
||||
/// The url-safe slug.
|
||||
#[sea_orm(indexed, unique)]
|
||||
pub web_slug: String,
|
||||
|
||||
/// Seasons that are part of this show
|
||||
/// Seasons that are part of this show.
|
||||
#[sea_orm(has_many)]
|
||||
pub seasons: HasMany<super::seasons::Entity>,
|
||||
/// Episodes that are part of this show
|
||||
/// Episodes that are part of this show.
|
||||
#[sea_orm(has_many)]
|
||||
pub episodes: HasMany<super::episodes::Entity>,
|
||||
|
||||
/// Potential content for this show
|
||||
/// Potential content for this show.
|
||||
#[sea_orm(has_one)]
|
||||
pub content: HasOne<entity::content::shows::Entity>,
|
||||
}
|
||||
|
||||
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
}
|
||||
|
||||
/// Season entity
|
||||
/// Season entity.
|
||||
pub mod seasons {
|
||||
use flix_model::id::ShowId;
|
||||
use flix_model::numbers::SeasonNumber;
|
||||
@@ -147,26 +150,26 @@ pub mod seasons {
|
||||
|
||||
use crate::entity;
|
||||
|
||||
/// The database representation of a flix season
|
||||
/// The database representation of a flix season.
|
||||
#[sea_orm::model]
|
||||
#[derive(Debug, Clone, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "flix_info_seasons")]
|
||||
pub struct Model {
|
||||
/// The season's show's ID
|
||||
/// The season's show's ID.
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub show_id: ShowId,
|
||||
/// The season's number
|
||||
/// The season's number.
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub season_number: SeasonNumber,
|
||||
/// The season's title
|
||||
/// The season's title.
|
||||
pub title: String,
|
||||
/// The season's overview
|
||||
/// The season's overview.
|
||||
pub overview: String,
|
||||
/// The season's air date
|
||||
/// The season's air date.
|
||||
#[sea_orm(indexed)]
|
||||
pub date: NaiveDate,
|
||||
|
||||
/// The show this season belongs to
|
||||
/// The show this season belongs to.
|
||||
#[sea_orm(
|
||||
belongs_to,
|
||||
from = "show_id",
|
||||
@@ -175,19 +178,20 @@ pub mod seasons {
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
pub show: HasOne<super::shows::Entity>,
|
||||
/// Episodes that are part of this season
|
||||
/// Episodes that are part of this season.
|
||||
#[sea_orm(has_many)]
|
||||
pub episodes: HasMany<super::episodes::Entity>,
|
||||
|
||||
/// Potential content for this season
|
||||
/// Potential content for this season.
|
||||
#[sea_orm(has_one)]
|
||||
pub content: HasOne<entity::content::seasons::Entity>,
|
||||
}
|
||||
|
||||
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
}
|
||||
|
||||
/// Episode entity
|
||||
/// Episode entity.
|
||||
pub mod episodes {
|
||||
use flix_model::id::ShowId;
|
||||
use flix_model::numbers::{EpisodeNumber, SeasonNumber};
|
||||
@@ -197,29 +201,29 @@ pub mod episodes {
|
||||
|
||||
use crate::entity;
|
||||
|
||||
/// The database representation of a flix episode
|
||||
/// The database representation of a flix episode.
|
||||
#[sea_orm::model]
|
||||
#[derive(Debug, Clone, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "flix_info_episodes")]
|
||||
pub struct Model {
|
||||
/// The episode's show's ID
|
||||
/// The episode's show's ID.
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub show_id: ShowId,
|
||||
/// The episode's season's number
|
||||
/// The episode's season's number.
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub season_number: SeasonNumber,
|
||||
/// The episode's number
|
||||
/// The episode's number.
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub episode_number: EpisodeNumber,
|
||||
/// The episode's title
|
||||
/// The episode's title.
|
||||
pub title: String,
|
||||
/// The episode's overview
|
||||
/// The episode's overview.
|
||||
pub overview: String,
|
||||
/// The episode's air date
|
||||
/// The episode's air date.
|
||||
#[sea_orm(indexed)]
|
||||
pub date: NaiveDate,
|
||||
|
||||
/// The show this episode belongs to
|
||||
/// The show this episode belongs to.
|
||||
#[sea_orm(
|
||||
belongs_to,
|
||||
from = "show_id",
|
||||
@@ -228,7 +232,7 @@ pub mod episodes {
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
pub show: HasOne<super::shows::Entity>,
|
||||
/// The season this episode belongs to
|
||||
/// The season this episode belongs to.
|
||||
#[sea_orm(
|
||||
belongs_to,
|
||||
from = "(show_id, season_number)",
|
||||
@@ -238,101 +242,112 @@ pub mod episodes {
|
||||
)]
|
||||
pub season: HasOne<super::seasons::Entity>,
|
||||
|
||||
/// Potential content for this episode
|
||||
/// Potential content for this episode.
|
||||
#[sea_orm(has_one)]
|
||||
pub content: HasOne<entity::content::episodes::Entity>,
|
||||
}
|
||||
|
||||
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
}
|
||||
|
||||
/// Macros for creating info entities
|
||||
/// Macros for creating info entities.
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
macro_rules! make_info_collection {
|
||||
($db:expr, $id:expr) => {
|
||||
$crate::entity::info::collections::ActiveModel {
|
||||
id: Set(::flix_model::id::CollectionId::from_raw($id)),
|
||||
title: Set(::std::string::String::new()),
|
||||
overview: Set(::std::string::String::new()),
|
||||
sort_title: Set(::std::string::String::new()),
|
||||
fs_slug: Set(format!("C FS {}", $id)),
|
||||
web_slug: Set(format!("C Web {}", $id)),
|
||||
}
|
||||
.insert($db)
|
||||
.await
|
||||
.expect("insert");
|
||||
drop(
|
||||
$crate::entity::info::collections::ActiveModel {
|
||||
id: Set(::flix_model::id::CollectionId::from_raw($id)),
|
||||
title: Set(::alloc::string::String::new()),
|
||||
overview: Set(::alloc::string::String::new()),
|
||||
sort_title: Set(::alloc::string::String::new()),
|
||||
fs_slug: Set(format!("C FS {}", $id)),
|
||||
web_slug: Set(format!("C Web {}", $id)),
|
||||
}
|
||||
.insert($db)
|
||||
.await
|
||||
.expect("insert"),
|
||||
);
|
||||
};
|
||||
}
|
||||
pub(crate) use make_info_collection;
|
||||
|
||||
macro_rules! make_info_movie {
|
||||
($db:expr, $id:expr) => {
|
||||
$crate::entity::info::movies::ActiveModel {
|
||||
id: Set(::flix_model::id::MovieId::from_raw($id)),
|
||||
title: Set(::std::string::String::new()),
|
||||
tagline: Set(::std::string::String::new()),
|
||||
overview: Set(::std::string::String::new()),
|
||||
date: Set(::chrono::NaiveDate::from_yo_opt(1, 1).expect("from_yo_opt")),
|
||||
sort_title: Set(::std::string::String::new()),
|
||||
fs_slug: Set(format!("M FS {}", $id)),
|
||||
web_slug: Set(format!("M Web {}", $id)),
|
||||
}
|
||||
.insert($db)
|
||||
.await
|
||||
.expect("insert");
|
||||
drop(
|
||||
$crate::entity::info::movies::ActiveModel {
|
||||
id: Set(::flix_model::id::MovieId::from_raw($id)),
|
||||
title: Set(::alloc::string::String::new()),
|
||||
tagline: Set(::alloc::string::String::new()),
|
||||
overview: Set(::alloc::string::String::new()),
|
||||
date: Set(::chrono::NaiveDate::from_yo_opt(1, 1).expect("from_yo_opt")),
|
||||
sort_title: Set(::alloc::string::String::new()),
|
||||
fs_slug: Set(format!("M FS {}", $id)),
|
||||
web_slug: Set(format!("M Web {}", $id)),
|
||||
}
|
||||
.insert($db)
|
||||
.await
|
||||
.expect("insert"),
|
||||
);
|
||||
};
|
||||
}
|
||||
pub(crate) use make_info_movie;
|
||||
|
||||
macro_rules! make_info_show {
|
||||
($db:expr, $id:expr) => {
|
||||
$crate::entity::info::shows::ActiveModel {
|
||||
id: Set(::flix_model::id::ShowId::from_raw($id)),
|
||||
title: Set(::std::string::String::new()),
|
||||
tagline: Set(::std::string::String::new()),
|
||||
overview: Set(::std::string::String::new()),
|
||||
date: Set(::chrono::NaiveDate::from_yo_opt(1, 1).expect("from_yo_opt")),
|
||||
sort_title: Set(::std::string::String::new()),
|
||||
fs_slug: Set(format!("S FS {}", $id)),
|
||||
web_slug: Set(format!("S Web {}", $id)),
|
||||
}
|
||||
.insert($db)
|
||||
.await
|
||||
.expect("insert");
|
||||
drop(
|
||||
$crate::entity::info::shows::ActiveModel {
|
||||
id: Set(::flix_model::id::ShowId::from_raw($id)),
|
||||
title: Set(::alloc::string::String::new()),
|
||||
tagline: Set(::alloc::string::String::new()),
|
||||
overview: Set(::alloc::string::String::new()),
|
||||
date: Set(::chrono::NaiveDate::from_yo_opt(1, 1).expect("from_yo_opt")),
|
||||
sort_title: Set(::alloc::string::String::new()),
|
||||
fs_slug: Set(format!("S FS {}", $id)),
|
||||
web_slug: Set(format!("S Web {}", $id)),
|
||||
}
|
||||
.insert($db)
|
||||
.await
|
||||
.expect("insert"),
|
||||
);
|
||||
};
|
||||
}
|
||||
pub(crate) use make_info_show;
|
||||
|
||||
macro_rules! make_info_season {
|
||||
($db:expr, $show:expr, $season:expr) => {
|
||||
$crate::entity::info::seasons::ActiveModel {
|
||||
show_id: Set(::flix_model::id::ShowId::from_raw($show)),
|
||||
season_number: Set(::flix_model::numbers::SeasonNumber::new($season)),
|
||||
title: Set(::std::string::String::new()),
|
||||
overview: Set(::std::string::String::new()),
|
||||
date: Set(::chrono::NaiveDate::from_yo_opt(1, 1).expect("from_yo_opt")),
|
||||
}
|
||||
.insert($db)
|
||||
.await
|
||||
.expect("insert");
|
||||
drop(
|
||||
$crate::entity::info::seasons::ActiveModel {
|
||||
show_id: Set(::flix_model::id::ShowId::from_raw($show)),
|
||||
season_number: Set(::flix_model::numbers::SeasonNumber::new($season)),
|
||||
title: Set(::alloc::string::String::new()),
|
||||
overview: Set(::alloc::string::String::new()),
|
||||
date: Set(::chrono::NaiveDate::from_yo_opt(1, 1).expect("from_yo_opt")),
|
||||
}
|
||||
.insert($db)
|
||||
.await
|
||||
.expect("insert"),
|
||||
);
|
||||
};
|
||||
}
|
||||
pub(crate) use make_info_season;
|
||||
|
||||
macro_rules! make_info_episode {
|
||||
($db:expr, $show:expr, $season:expr, $episode:expr) => {
|
||||
$crate::entity::info::episodes::ActiveModel {
|
||||
show_id: Set(::flix_model::id::ShowId::from_raw($show)),
|
||||
season_number: Set(::flix_model::numbers::SeasonNumber::new($season)),
|
||||
episode_number: Set(::flix_model::numbers::EpisodeNumber::new($episode)),
|
||||
title: Set(::std::string::String::new()),
|
||||
overview: Set(::std::string::String::new()),
|
||||
date: Set(::chrono::NaiveDate::from_yo_opt(1, 1).expect("from_yo_opt")),
|
||||
}
|
||||
.insert($db)
|
||||
.await
|
||||
.expect("insert");
|
||||
drop(
|
||||
$crate::entity::info::episodes::ActiveModel {
|
||||
show_id: Set(::flix_model::id::ShowId::from_raw($show)),
|
||||
season_number: Set(::flix_model::numbers::SeasonNumber::new($season)),
|
||||
episode_number: Set(::flix_model::numbers::EpisodeNumber::new($episode)),
|
||||
title: Set(::alloc::string::String::new()),
|
||||
overview: Set(::alloc::string::String::new()),
|
||||
date: Set(::chrono::NaiveDate::from_yo_opt(1, 1).expect("from_yo_opt")),
|
||||
}
|
||||
.insert($db)
|
||||
.await
|
||||
.expect("insert"),
|
||||
);
|
||||
};
|
||||
}
|
||||
pub(crate) use make_info_episode;
|
||||
@@ -355,7 +370,10 @@ mod tests {
|
||||
make_info_collection, make_info_episode, make_info_movie, make_info_season, make_info_show,
|
||||
};
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[tokio::test]
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
#[expect(clippy::default_numeric_fallback, reason = "unit test")]
|
||||
async fn use_test_macros() {
|
||||
let db = new_initialized_memory_db().await;
|
||||
|
||||
@@ -366,8 +384,10 @@ mod tests {
|
||||
make_info_episode!(&db, 1, 1, 1);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[tokio::test]
|
||||
async fn test_round_trip_collections() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
async fn round_trip_collections() {
|
||||
let db = new_initialized_memory_db().await;
|
||||
|
||||
macro_rules! assert_collection {
|
||||
@@ -409,8 +429,10 @@ mod tests {
|
||||
assert_collection!(&db, 8, NotNullViolation; web_slug);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[tokio::test]
|
||||
async fn test_round_trip_movies() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
async fn round_trip_movies() {
|
||||
let db = new_initialized_memory_db().await;
|
||||
|
||||
macro_rules! assert_movie {
|
||||
@@ -458,8 +480,10 @@ mod tests {
|
||||
assert_movie!(&db, 10, NotNullViolation; web_slug);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[tokio::test]
|
||||
async fn test_round_trip_shows() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
async fn round_trip_shows() {
|
||||
let db = new_initialized_memory_db().await;
|
||||
|
||||
macro_rules! assert_show {
|
||||
@@ -510,8 +534,11 @@ mod tests {
|
||||
assert_show!(&db, 10, NotNullViolation; web_slug);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[tokio::test]
|
||||
async fn test_round_trip_seasons() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
#[expect(clippy::default_numeric_fallback, reason = "unit test")]
|
||||
async fn round_trip_seasons() {
|
||||
let db = new_initialized_memory_db().await;
|
||||
|
||||
macro_rules! assert_season {
|
||||
@@ -561,8 +588,11 @@ mod tests {
|
||||
assert_season!(&db, 1, 7, NotNullViolation; date);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[tokio::test]
|
||||
async fn test_round_trip_episodes() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
#[expect(clippy::default_numeric_fallback, reason = "unit test")]
|
||||
async fn round_trip_episodes() {
|
||||
let db = new_initialized_memory_db().await;
|
||||
|
||||
macro_rules! assert_episode {
|
||||
|
||||
+113
-63
@@ -1,4 +1,10 @@
|
||||
//! Entity structs for interacting with the database
|
||||
//! Entity structs for interacting with the database.
|
||||
|
||||
#![expect(clippy::exhaustive_enums, reason = "reflects the database schema")]
|
||||
#![expect(clippy::exhaustive_structs, reason = "reflects the database schema")]
|
||||
#![expect(clippy::derive_partial_eq_without_eq, reason = "#[sea_orm::model]")]
|
||||
#![expect(clippy::impl_trait_in_params, reason = "#[sea_orm::model]")]
|
||||
#![expect(clippy::same_name_method, reason = "#[sea_orm::model]")]
|
||||
|
||||
pub mod content;
|
||||
pub mod info;
|
||||
@@ -13,8 +19,6 @@ mod tests {
|
||||
use flix_tmdb::model::id::TmdbRepr as TmdbReprId;
|
||||
|
||||
use sea_orm::ActiveValue::Set;
|
||||
use sea_orm::DatabaseConnection;
|
||||
use sea_orm::DbErr;
|
||||
use sea_orm::entity::prelude::*;
|
||||
use sea_orm::sqlx::error::ErrorKind;
|
||||
use seamantic::model::id::SeaOrmRepr as SeaOrmReprId;
|
||||
@@ -32,35 +36,36 @@ mod tests {
|
||||
use crate::tests::new_initialized_memory_db;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ErrorKindError {
|
||||
NotRuntimeError,
|
||||
NotSqlxError,
|
||||
NotDatabaseError,
|
||||
pub(crate) enum ErrorKindNot {
|
||||
Runtime,
|
||||
Sqlx,
|
||||
Database,
|
||||
}
|
||||
|
||||
pub fn get_error_kind(error: DbErr) -> Result<ErrorKind, ErrorKindError> {
|
||||
let runtime_err = match error {
|
||||
DbErr::Conn(runtime_err) => runtime_err,
|
||||
DbErr::Exec(runtime_err) => runtime_err,
|
||||
DbErr::Query(runtime_err) => runtime_err,
|
||||
_ => return Err(ErrorKindError::NotRuntimeError),
|
||||
/// Extract the error kind from a [`DbErr`].
|
||||
///
|
||||
/// # Errors
|
||||
/// If the [`DbErr`] internals diverge from [`SqlxError::Database`].
|
||||
pub(crate) fn get_error_kind(error: DbErr) -> Result<ErrorKind, ErrorKindNot> {
|
||||
let (DbErr::Conn(runtime_err) | DbErr::Exec(runtime_err) | DbErr::Query(runtime_err)) =
|
||||
error
|
||||
else {
|
||||
return Err(ErrorKindNot::Runtime);
|
||||
};
|
||||
|
||||
let sqlx_err = match runtime_err {
|
||||
sea_orm::RuntimeErr::SqlxError(sqlx_err) => sqlx_err,
|
||||
_ => return Err(ErrorKindError::NotSqlxError),
|
||||
let RuntimeErr::SqlxError(sqlx_err) = runtime_err else {
|
||||
return Err(ErrorKindNot::Sqlx);
|
||||
};
|
||||
|
||||
let database_err = match sqlx_err.as_ref() {
|
||||
sea_orm::SqlxError::Database(database_err) => database_err,
|
||||
_ => return Err(ErrorKindError::NotDatabaseError),
|
||||
let SqlxError::Database(database_err) = sqlx_err.as_ref() else {
|
||||
return Err(ErrorKindNot::Database);
|
||||
};
|
||||
|
||||
Ok(database_err.kind())
|
||||
}
|
||||
|
||||
/// Helper macro for writing tests for `ActiveModel` structs where
|
||||
/// toggling [sea_orm::ActiveValue] is needed
|
||||
/// toggling [`sea_orm::ActiveValue`] is needed.
|
||||
macro_rules! notsettable {
|
||||
($field:ident, $value:expr $(, $($skip:ident),+)?) => {
|
||||
if notsettable!(@skip, $field $(, $($skip),+)?) {
|
||||
@@ -76,7 +81,7 @@ mod tests {
|
||||
pub(super) use notsettable;
|
||||
|
||||
/// Helper macro for writing tests for `ActiveModel` structs where
|
||||
/// toggling [sea_orm::ActiveValue] is needed
|
||||
/// toggling [`sea_orm::ActiveValue`] is needed.
|
||||
macro_rules! noneable {
|
||||
($field:ident, $value:expr $(, $($skip:ident),+)?) => {
|
||||
if noneable!(@skip, $field $(, $($skip),+)?) {
|
||||
@@ -148,6 +153,11 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize a database with data to be deleted.
|
||||
///
|
||||
/// # Panics
|
||||
/// If any database operation fails, since this is only for testing.
|
||||
#[expect(clippy::default_numeric_fallback, reason = "unit test")]
|
||||
async fn initialize_deletion_test_database(id: &DbId) -> DatabaseConnection {
|
||||
let db = new_initialized_memory_db().await;
|
||||
|
||||
@@ -284,12 +294,14 @@ mod tests {
|
||||
db
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[tokio::test]
|
||||
async fn test_delete_info_collection() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
async fn delete_info_collection() {
|
||||
let id = DbId::default();
|
||||
let db = initialize_deletion_test_database(&id).await;
|
||||
|
||||
entity::info::collections::Entity::delete_by_id(seamantic::model::id::Id::from_raw(
|
||||
_ = entity::info::collections::Entity::delete_by_id(seamantic::model::id::Id::from_raw(
|
||||
id.flix.collection,
|
||||
))
|
||||
.exec(&db)
|
||||
@@ -368,12 +380,14 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[tokio::test]
|
||||
async fn test_delete_info_movie() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
async fn delete_info_movie() {
|
||||
let id = DbId::default();
|
||||
let db = initialize_deletion_test_database(&id).await;
|
||||
|
||||
entity::info::movies::Entity::delete_by_id(seamantic::model::id::Id::from_raw(
|
||||
_ = entity::info::movies::Entity::delete_by_id(seamantic::model::id::Id::from_raw(
|
||||
id.flix.movie,
|
||||
))
|
||||
.exec(&db)
|
||||
@@ -452,15 +466,19 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[tokio::test]
|
||||
async fn test_delete_info_show() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
async fn delete_info_show() {
|
||||
let id = DbId::default();
|
||||
let db = initialize_deletion_test_database(&id).await;
|
||||
|
||||
entity::info::shows::Entity::delete_by_id(seamantic::model::id::Id::from_raw(id.flix.show))
|
||||
.exec(&db)
|
||||
.await
|
||||
.expect("Entity::delete_by_id");
|
||||
_ = entity::info::shows::Entity::delete_by_id(seamantic::model::id::Id::from_raw(
|
||||
id.flix.show,
|
||||
))
|
||||
.exec(&db)
|
||||
.await
|
||||
.expect("Entity::delete_by_id");
|
||||
|
||||
assert_eq!(
|
||||
Ok(1),
|
||||
@@ -534,12 +552,14 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[tokio::test]
|
||||
async fn test_delete_info_season() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
async fn delete_info_season() {
|
||||
let id = DbId::default();
|
||||
let db = initialize_deletion_test_database(&id).await;
|
||||
|
||||
entity::info::seasons::Entity::delete_by_id((
|
||||
_ = entity::info::seasons::Entity::delete_by_id((
|
||||
seamantic::model::id::Id::from_raw(id.flix.show),
|
||||
SeasonNumber::new(id.flix.season),
|
||||
))
|
||||
@@ -619,12 +639,14 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[tokio::test]
|
||||
async fn test_delete_info_episodes() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
async fn delete_info_episodes() {
|
||||
let id = DbId::default();
|
||||
let db = initialize_deletion_test_database(&id).await;
|
||||
|
||||
entity::info::episodes::Entity::delete_by_id((
|
||||
_ = entity::info::episodes::Entity::delete_by_id((
|
||||
seamantic::model::id::Id::from_raw(id.flix.show),
|
||||
SeasonNumber::new(id.flix.season),
|
||||
EpisodeNumber::new(id.flix.episode),
|
||||
@@ -705,13 +727,15 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[cfg(feature = "tmdb")]
|
||||
#[tokio::test]
|
||||
async fn test_delete_tmdb_collection() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
async fn delete_tmdb_collection() {
|
||||
let id = DbId::default();
|
||||
let db = initialize_deletion_test_database(&id).await;
|
||||
|
||||
entity::tmdb::collections::Entity::delete_by_id(flix_tmdb::model::id::Id::from_raw(
|
||||
_ = entity::tmdb::collections::Entity::delete_by_id(flix_tmdb::model::id::Id::from_raw(
|
||||
id.tmdb.collection,
|
||||
))
|
||||
.exec(&db)
|
||||
@@ -790,13 +814,15 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[cfg(feature = "tmdb")]
|
||||
#[tokio::test]
|
||||
async fn test_delete_tmdb_movie() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
async fn delete_tmdb_movie() {
|
||||
let id = DbId::default();
|
||||
let db = initialize_deletion_test_database(&id).await;
|
||||
|
||||
entity::tmdb::movies::Entity::delete_by_id(flix_tmdb::model::id::Id::from_raw(
|
||||
_ = entity::tmdb::movies::Entity::delete_by_id(flix_tmdb::model::id::Id::from_raw(
|
||||
id.tmdb.movie,
|
||||
))
|
||||
.exec(&db)
|
||||
@@ -875,16 +901,20 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[cfg(feature = "tmdb")]
|
||||
#[tokio::test]
|
||||
async fn test_delete_tmdb_show() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
async fn delete_tmdb_show() {
|
||||
let id = DbId::default();
|
||||
let db = initialize_deletion_test_database(&id).await;
|
||||
|
||||
entity::tmdb::shows::Entity::delete_by_id(flix_tmdb::model::id::Id::from_raw(id.tmdb.show))
|
||||
.exec(&db)
|
||||
.await
|
||||
.expect("Entity::delete_by_id");
|
||||
_ = entity::tmdb::shows::Entity::delete_by_id(flix_tmdb::model::id::Id::from_raw(
|
||||
id.tmdb.show,
|
||||
))
|
||||
.exec(&db)
|
||||
.await
|
||||
.expect("Entity::delete_by_id");
|
||||
|
||||
assert_eq!(
|
||||
Ok(1),
|
||||
@@ -958,13 +988,15 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[cfg(feature = "tmdb")]
|
||||
#[tokio::test]
|
||||
async fn test_delete_tmdb_season() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
async fn delete_tmdb_season() {
|
||||
let id = DbId::default();
|
||||
let db = initialize_deletion_test_database(&id).await;
|
||||
|
||||
entity::tmdb::seasons::Entity::delete_by_id((
|
||||
_ = entity::tmdb::seasons::Entity::delete_by_id((
|
||||
flix_tmdb::model::id::Id::from_raw(id.tmdb.show),
|
||||
SeasonNumber::new(id.tmdb.season),
|
||||
))
|
||||
@@ -1044,13 +1076,15 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[cfg(feature = "tmdb")]
|
||||
#[tokio::test]
|
||||
async fn test_delete_tmdb_episode() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
async fn delete_tmdb_episode() {
|
||||
let id = DbId::default();
|
||||
let db = initialize_deletion_test_database(&id).await;
|
||||
|
||||
entity::tmdb::episodes::Entity::delete_by_id((
|
||||
_ = entity::tmdb::episodes::Entity::delete_by_id((
|
||||
flix_tmdb::model::id::Id::from_raw(id.tmdb.show),
|
||||
SeasonNumber::new(id.tmdb.season),
|
||||
EpisodeNumber::new(id.tmdb.episode),
|
||||
@@ -1131,12 +1165,14 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[tokio::test]
|
||||
async fn test_delete_content_library() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
async fn delete_content_library() {
|
||||
let id = DbId::default();
|
||||
let db = initialize_deletion_test_database(&id).await;
|
||||
|
||||
entity::content::libraries::Entity::delete_by_id(seamantic::model::id::Id::from_raw(
|
||||
_ = entity::content::libraries::Entity::delete_by_id(seamantic::model::id::Id::from_raw(
|
||||
id.content.library,
|
||||
))
|
||||
.exec(&db)
|
||||
@@ -1215,12 +1251,14 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[tokio::test]
|
||||
async fn test_delete_content_collection() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
async fn delete_content_collection() {
|
||||
let id = DbId::default();
|
||||
let db = initialize_deletion_test_database(&id).await;
|
||||
|
||||
entity::content::collections::Entity::delete_by_id(seamantic::model::id::Id::from_raw(
|
||||
_ = entity::content::collections::Entity::delete_by_id(seamantic::model::id::Id::from_raw(
|
||||
id.flix.collection,
|
||||
))
|
||||
.exec(&db)
|
||||
@@ -1299,12 +1337,14 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[tokio::test]
|
||||
async fn test_delete_content_movie() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
async fn delete_content_movie() {
|
||||
let id = DbId::default();
|
||||
let db = initialize_deletion_test_database(&id).await;
|
||||
|
||||
entity::content::movies::Entity::delete_by_id(seamantic::model::id::Id::from_raw(
|
||||
_ = entity::content::movies::Entity::delete_by_id(seamantic::model::id::Id::from_raw(
|
||||
id.flix.movie,
|
||||
))
|
||||
.exec(&db)
|
||||
@@ -1383,12 +1423,14 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[tokio::test]
|
||||
async fn test_delete_content_show() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
async fn delete_content_show() {
|
||||
let id = DbId::default();
|
||||
let db = initialize_deletion_test_database(&id).await;
|
||||
|
||||
entity::content::shows::Entity::delete_by_id(seamantic::model::id::Id::from_raw(
|
||||
_ = entity::content::shows::Entity::delete_by_id(seamantic::model::id::Id::from_raw(
|
||||
id.flix.show,
|
||||
))
|
||||
.exec(&db)
|
||||
@@ -1467,12 +1509,14 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[tokio::test]
|
||||
async fn test_delete_content_season() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
async fn delete_content_season() {
|
||||
let id = DbId::default();
|
||||
let db = initialize_deletion_test_database(&id).await;
|
||||
|
||||
entity::content::seasons::Entity::delete_by_id((
|
||||
_ = entity::content::seasons::Entity::delete_by_id((
|
||||
seamantic::model::id::Id::from_raw(id.flix.show),
|
||||
SeasonNumber::new(id.flix.season),
|
||||
))
|
||||
@@ -1552,12 +1596,14 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[tokio::test]
|
||||
async fn test_delete_content_episode() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
async fn delete_content_episode() {
|
||||
let id = DbId::default();
|
||||
let db = initialize_deletion_test_database(&id).await;
|
||||
|
||||
entity::content::episodes::Entity::delete_by_id((
|
||||
_ = entity::content::episodes::Entity::delete_by_id((
|
||||
seamantic::model::id::Id::from_raw(id.flix.show),
|
||||
SeasonNumber::new(id.flix.season),
|
||||
EpisodeNumber::new(id.flix.episode),
|
||||
@@ -1638,12 +1684,14 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[tokio::test]
|
||||
async fn test_delete_watched_movie() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
async fn delete_watched_movie() {
|
||||
let id = DbId::default();
|
||||
let db = initialize_deletion_test_database(&id).await;
|
||||
|
||||
entity::watched::movies::Entity::delete_by_id((
|
||||
_ = entity::watched::movies::Entity::delete_by_id((
|
||||
seamantic::model::id::Id::from_raw(id.flix.movie),
|
||||
id.watch.user,
|
||||
))
|
||||
@@ -1723,12 +1771,14 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[tokio::test]
|
||||
async fn test_delete_watched_episode() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
async fn delete_watched_episode() {
|
||||
let id = DbId::default();
|
||||
let db = initialize_deletion_test_database(&id).await;
|
||||
|
||||
entity::watched::episodes::Entity::delete_by_id((
|
||||
_ = entity::watched::episodes::Entity::delete_by_id((
|
||||
seamantic::model::id::Id::from_raw(id.flix.show),
|
||||
SeasonNumber::new(id.flix.season),
|
||||
EpisodeNumber::new(id.flix.episode),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! This module contains entities for storing dynamic data from TMDB
|
||||
//! This module contains entities for storing dynamic data from TMDB.
|
||||
|
||||
/// Collection entity
|
||||
/// Collection entity.
|
||||
pub mod collections {
|
||||
use flix_model::id::CollectionId as FlixId;
|
||||
use flix_tmdb::model::id::CollectionId;
|
||||
@@ -10,23 +10,23 @@ pub mod collections {
|
||||
|
||||
use crate::entity;
|
||||
|
||||
/// The database representation of a tmdb collection
|
||||
/// The database representation of a tmdb collection.
|
||||
#[sea_orm::model]
|
||||
#[derive(Debug, Clone, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "flix_tmdb_collections")]
|
||||
pub struct Model {
|
||||
/// The collection's TMDB ID
|
||||
/// The collection's TMDB ID.
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub tmdb_id: CollectionId,
|
||||
/// The collection's ID
|
||||
/// The collection's ID.
|
||||
#[sea_orm(unique)]
|
||||
pub flix_id: FlixId,
|
||||
/// The date of the last update
|
||||
/// The date of the last update.
|
||||
pub last_update: DateTime<Utc>,
|
||||
/// The number of movies in the collection
|
||||
/// The number of movies in the collection.
|
||||
pub movie_count: u16,
|
||||
|
||||
/// The info for this collection
|
||||
/// The info for this collection.
|
||||
#[sea_orm(
|
||||
belongs_to,
|
||||
from = "flix_id",
|
||||
@@ -36,15 +36,16 @@ pub mod collections {
|
||||
)]
|
||||
pub info: HasOne<entity::info::collections::Entity>,
|
||||
|
||||
/// Movies that are in this collection
|
||||
/// Movies that are in this collection.
|
||||
#[sea_orm(has_many)]
|
||||
pub movies: HasMany<super::movies::Entity>,
|
||||
}
|
||||
|
||||
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
}
|
||||
|
||||
/// Movie entity
|
||||
/// Movie entity.
|
||||
pub mod movies {
|
||||
use flix_model::id::MovieId as FlixId;
|
||||
use flix_tmdb::model::id::{CollectionId, MovieId};
|
||||
@@ -56,26 +57,26 @@ pub mod movies {
|
||||
|
||||
use crate::entity;
|
||||
|
||||
/// The database representation of a tmdb movie
|
||||
/// The database representation of a tmdb movie.
|
||||
#[sea_orm::model]
|
||||
#[derive(Debug, Clone, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "flix_tmdb_movies")]
|
||||
pub struct Model {
|
||||
/// The movie's TMDB ID
|
||||
/// The movie's TMDB ID.
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub tmdb_id: MovieId,
|
||||
/// The movie's ID
|
||||
/// The movie's ID.
|
||||
#[sea_orm(unique)]
|
||||
pub flix_id: FlixId,
|
||||
/// The date of the last update
|
||||
/// The date of the last update.
|
||||
pub last_update: DateTime<Utc>,
|
||||
/// The movie's runtime in seconds
|
||||
/// The movie's runtime in seconds.
|
||||
pub runtime: Seconds,
|
||||
/// The TMDB ID of the collection this movie belongs to
|
||||
/// The TMDB ID of the collection this movie belongs to.
|
||||
#[sea_orm(indexed)]
|
||||
pub collection_id: Option<CollectionId>,
|
||||
|
||||
/// The collection this movie belongs to
|
||||
/// The collection this movie belongs to.
|
||||
#[sea_orm(
|
||||
belongs_to,
|
||||
from = "collection_id",
|
||||
@@ -84,7 +85,7 @@ pub mod movies {
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
pub collection: HasOne<super::collections::Entity>,
|
||||
/// The info for this movie
|
||||
/// The info for this movie.
|
||||
#[sea_orm(
|
||||
belongs_to,
|
||||
from = "flix_id",
|
||||
@@ -95,10 +96,11 @@ pub mod movies {
|
||||
pub info: HasOne<entity::info::movies::Entity>,
|
||||
}
|
||||
|
||||
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
}
|
||||
|
||||
/// Show entity
|
||||
/// Show entity.
|
||||
pub mod shows {
|
||||
use flix_model::id::ShowId as FlixId;
|
||||
use flix_tmdb::model::id::ShowId;
|
||||
@@ -108,23 +110,23 @@ pub mod shows {
|
||||
|
||||
use crate::entity;
|
||||
|
||||
/// The database representation of a tmdb show
|
||||
/// The database representation of a tmdb show.
|
||||
#[sea_orm::model]
|
||||
#[derive(Debug, Clone, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "flix_tmdb_shows")]
|
||||
pub struct Model {
|
||||
/// The show's TMDB ID
|
||||
/// The show's TMDB ID.
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub tmdb_id: ShowId,
|
||||
/// The show's ID
|
||||
/// The show's ID.
|
||||
#[sea_orm(unique)]
|
||||
pub flix_id: FlixId,
|
||||
/// The movie's runtime in seconds
|
||||
/// The movie's runtime in seconds.
|
||||
pub last_update: DateTime<Utc>,
|
||||
/// The number of seasons the show has
|
||||
/// The number of seasons the show has.
|
||||
pub number_of_seasons: u32,
|
||||
|
||||
/// The info for this show
|
||||
/// The info for this show.
|
||||
#[sea_orm(
|
||||
belongs_to,
|
||||
from = "flix_id",
|
||||
@@ -134,18 +136,19 @@ pub mod shows {
|
||||
)]
|
||||
pub info: HasOne<entity::info::shows::Entity>,
|
||||
|
||||
/// Seasons that are part of this show
|
||||
/// Seasons that are part of this show.
|
||||
#[sea_orm(has_many)]
|
||||
pub seasons: HasMany<super::seasons::Entity>,
|
||||
/// Episodes that are part of this show
|
||||
/// Episodes that are part of this show.
|
||||
#[sea_orm(has_many)]
|
||||
pub episodes: HasMany<super::episodes::Entity>,
|
||||
}
|
||||
|
||||
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
}
|
||||
|
||||
/// Season entity
|
||||
/// Season entity.
|
||||
pub mod seasons {
|
||||
use flix_model::id::ShowId as FlixId;
|
||||
use flix_model::numbers::SeasonNumber;
|
||||
@@ -156,27 +159,27 @@ pub mod seasons {
|
||||
|
||||
use crate::entity;
|
||||
|
||||
/// The database representation of a tmdb season
|
||||
/// The database representation of a tmdb season.
|
||||
#[sea_orm::model]
|
||||
#[derive(Debug, Clone, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "flix_tmdb_seasons")]
|
||||
pub struct Model {
|
||||
/// The season's show's TMDB ID
|
||||
/// The season's show's TMDB ID.
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub tmdb_show: ShowId,
|
||||
/// The season's TMDB season number
|
||||
/// The season's TMDB season number.
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub tmdb_season: SeasonNumber,
|
||||
/// The season's show's ID
|
||||
/// The season's show's ID.
|
||||
#[sea_orm(unique_key = "flix")]
|
||||
pub flix_show: FlixId,
|
||||
/// The season's number
|
||||
/// The season's number.
|
||||
#[sea_orm(unique_key = "flix")]
|
||||
pub flix_season: SeasonNumber,
|
||||
/// The date of the last update
|
||||
/// The date of the last update.
|
||||
pub last_update: DateTime<Utc>,
|
||||
|
||||
/// The show this season belongs to
|
||||
/// The show this season belongs to.
|
||||
#[sea_orm(
|
||||
belongs_to,
|
||||
from = "tmdb_show",
|
||||
@@ -185,7 +188,7 @@ pub mod seasons {
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
pub show: HasOne<super::shows::Entity>,
|
||||
/// The info for this season
|
||||
/// The info for this season.
|
||||
#[sea_orm(
|
||||
belongs_to,
|
||||
from = "(flix_show, flix_season)",
|
||||
@@ -195,15 +198,16 @@ pub mod seasons {
|
||||
)]
|
||||
pub info: HasOne<entity::info::seasons::Entity>,
|
||||
|
||||
/// Episodes that are part of this season
|
||||
/// Episodes that are part of this season.
|
||||
#[sea_orm(has_many)]
|
||||
pub episodes: HasMany<super::episodes::Entity>,
|
||||
}
|
||||
|
||||
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
}
|
||||
|
||||
/// Season entity
|
||||
/// Season entity.
|
||||
pub mod episodes {
|
||||
use flix_model::id::ShowId as FlixId;
|
||||
use flix_model::numbers::{EpisodeNumber, SeasonNumber};
|
||||
@@ -215,35 +219,35 @@ pub mod episodes {
|
||||
|
||||
use crate::entity;
|
||||
|
||||
/// The database representation of a tmdb episode
|
||||
/// The database representation of a tmdb episode.
|
||||
#[sea_orm::model]
|
||||
#[derive(Debug, Clone, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "flix_tmdb_episodes")]
|
||||
pub struct Model {
|
||||
/// The episode's show's TMDB ID
|
||||
/// The episode's show's TMDB ID.
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub tmdb_show: ShowId,
|
||||
/// The episode's season's TMDB season number
|
||||
/// The episode's season's TMDB season number.
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub tmdb_season: SeasonNumber,
|
||||
/// The episode's TMDB episode number
|
||||
/// The episode's TMDB episode number.
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub tmdb_episode: EpisodeNumber,
|
||||
/// The episode's show's ID
|
||||
/// The episode's show's ID.
|
||||
#[sea_orm(unique_key = "flix")]
|
||||
pub flix_show: FlixId,
|
||||
/// The episode's season's number
|
||||
/// The episode's season's number.
|
||||
#[sea_orm(unique_key = "flix")]
|
||||
pub flix_season: SeasonNumber,
|
||||
/// The episode's number
|
||||
/// The episode's number.
|
||||
#[sea_orm(unique_key = "flix")]
|
||||
pub flix_episode: EpisodeNumber,
|
||||
/// The date of the last update
|
||||
/// The date of the last update.
|
||||
pub last_update: DateTime<Utc>,
|
||||
/// The episode's runtime in seconds
|
||||
/// The episode's runtime in seconds.
|
||||
pub runtime: Seconds,
|
||||
|
||||
/// The show this episode belongs to
|
||||
/// The show this episode belongs to.
|
||||
#[sea_orm(
|
||||
belongs_to,
|
||||
from = "tmdb_show",
|
||||
@@ -252,7 +256,7 @@ pub mod episodes {
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
pub show: HasOne<super::shows::Entity>,
|
||||
/// The season this episode belongs to
|
||||
/// The season this episode belongs to.
|
||||
#[sea_orm(
|
||||
belongs_to,
|
||||
from = "(tmdb_show, tmdb_season)",
|
||||
@@ -261,7 +265,7 @@ pub mod episodes {
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
pub season: HasOne<super::seasons::Entity>,
|
||||
/// The info for this episode
|
||||
/// The info for this episode.
|
||||
#[sea_orm(
|
||||
belongs_to,
|
||||
from = "(flix_show, flix_season, flix_episode)",
|
||||
@@ -272,15 +276,16 @@ pub mod episodes {
|
||||
pub info: HasOne<entity::info::episodes::Entity>,
|
||||
}
|
||||
|
||||
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
}
|
||||
|
||||
/// Macros for creating tmdb entities
|
||||
/// Macros for creating tmdb entities.
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
macro_rules! make_tmdb_collection {
|
||||
($db:expr, $id:expr, $flix_id:expr) => {
|
||||
$crate::entity::tmdb::collections::ActiveModel {
|
||||
_ = $crate::entity::tmdb::collections::ActiveModel {
|
||||
tmdb_id: Set(::flix_tmdb::model::id::CollectionId::from_raw($id)),
|
||||
flix_id: Set(::flix_model::id::CollectionId::from_raw($flix_id)),
|
||||
last_update: Set(::chrono::Utc::now()),
|
||||
@@ -295,7 +300,7 @@ pub mod test {
|
||||
|
||||
macro_rules! make_tmdb_movie {
|
||||
($db:expr, $id:expr, $flix_id:expr) => {
|
||||
$crate::entity::tmdb::movies::ActiveModel {
|
||||
_ = $crate::entity::tmdb::movies::ActiveModel {
|
||||
tmdb_id: Set(::flix_tmdb::model::id::MovieId::from_raw($id)),
|
||||
flix_id: Set(::flix_model::id::MovieId::from_raw($flix_id)),
|
||||
last_update: Set(::chrono::Utc::now()),
|
||||
@@ -311,7 +316,7 @@ pub mod test {
|
||||
|
||||
macro_rules! make_tmdb_show {
|
||||
($db:expr, $id:expr, $flix_id:expr) => {
|
||||
$crate::entity::tmdb::shows::ActiveModel {
|
||||
_ = $crate::entity::tmdb::shows::ActiveModel {
|
||||
tmdb_id: Set(::flix_tmdb::model::id::ShowId::from_raw($id)),
|
||||
flix_id: Set(::flix_model::id::ShowId::from_raw($flix_id)),
|
||||
last_update: Set(::chrono::Utc::now()),
|
||||
@@ -326,7 +331,7 @@ pub mod test {
|
||||
|
||||
macro_rules! make_tmdb_season {
|
||||
($db:expr, $show:expr, $season:expr, $flix_show:expr, $flix_season:expr) => {
|
||||
$crate::entity::tmdb::seasons::ActiveModel {
|
||||
_ = $crate::entity::tmdb::seasons::ActiveModel {
|
||||
tmdb_show: Set(::flix_tmdb::model::id::ShowId::from_raw($show)),
|
||||
tmdb_season: Set(::flix_model::numbers::SeasonNumber::new($season)),
|
||||
flix_show: Set(::flix_model::id::ShowId::from_raw($flix_show)),
|
||||
@@ -342,7 +347,7 @@ pub mod test {
|
||||
|
||||
macro_rules! make_tmdb_episode {
|
||||
($db:expr, $show:expr, $season:expr, $episode:expr, $flix_show:expr, $flix_season:expr, $flix_episode:expr) => {
|
||||
$crate::entity::tmdb::episodes::ActiveModel {
|
||||
_ = $crate::entity::tmdb::episodes::ActiveModel {
|
||||
tmdb_show: Set(::flix_tmdb::model::id::ShowId::from_raw($show)),
|
||||
tmdb_season: Set(::flix_model::numbers::SeasonNumber::new($season)),
|
||||
tmdb_episode: Set(::flix_model::numbers::EpisodeNumber::new($episode)),
|
||||
@@ -385,7 +390,10 @@ mod tests {
|
||||
make_tmdb_collection, make_tmdb_episode, make_tmdb_movie, make_tmdb_season, make_tmdb_show,
|
||||
};
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[tokio::test]
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
#[expect(clippy::default_numeric_fallback, reason = "unit test")]
|
||||
async fn use_test_macros() {
|
||||
let db = new_initialized_memory_db().await;
|
||||
|
||||
@@ -402,8 +410,11 @@ mod tests {
|
||||
make_tmdb_episode!(&db, 1, 1, 1, 1, 1, 1);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[tokio::test]
|
||||
async fn test_round_trip_collections() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
#[expect(clippy::default_numeric_fallback, reason = "unit test")]
|
||||
async fn round_trip_collections() {
|
||||
let db = new_initialized_memory_db().await;
|
||||
|
||||
macro_rules! assert_collection {
|
||||
@@ -449,8 +460,11 @@ mod tests {
|
||||
assert_collection!(&db, 6, 6, NotNullViolation; movie_count);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[tokio::test]
|
||||
async fn test_round_trip_movies() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
#[expect(clippy::default_numeric_fallback, reason = "unit test")]
|
||||
async fn round_trip_movies() {
|
||||
let db = new_initialized_memory_db().await;
|
||||
|
||||
macro_rules! assert_movie {
|
||||
@@ -502,8 +516,11 @@ mod tests {
|
||||
assert_movie!(&db, 7, 7, None, ForeignKeyViolation; collection_id);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[tokio::test]
|
||||
async fn test_round_trip_shows() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
#[expect(clippy::default_numeric_fallback, reason = "unit test")]
|
||||
async fn round_trip_shows() {
|
||||
let db = new_initialized_memory_db().await;
|
||||
|
||||
macro_rules! assert_show {
|
||||
@@ -552,8 +569,11 @@ mod tests {
|
||||
assert_show!(&db, 6, 6, NotNullViolation; number_of_seasons);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[tokio::test]
|
||||
async fn test_round_trip_seasons() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
#[expect(clippy::default_numeric_fallback, reason = "unit test")]
|
||||
async fn round_trip_seasons() {
|
||||
let db = new_initialized_memory_db().await;
|
||||
|
||||
macro_rules! assert_season {
|
||||
@@ -607,8 +627,11 @@ mod tests {
|
||||
assert_season!(&db, 1, 7, 1, 7, NotNullViolation; last_update);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[tokio::test]
|
||||
async fn test_round_trip_episodes() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
#[expect(clippy::default_numeric_fallback, reason = "unit test")]
|
||||
async fn round_trip_episodes() {
|
||||
let db = new_initialized_memory_db().await;
|
||||
|
||||
macro_rules! assert_episode {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! This module contains entities for storing watched information
|
||||
//! This module contains entities for storing watched information.
|
||||
|
||||
/// Collection entity
|
||||
/// Collection entity.
|
||||
pub mod collections {
|
||||
use flix_model::id::{CollectionId, RawId};
|
||||
|
||||
@@ -9,21 +9,21 @@ pub mod collections {
|
||||
|
||||
use crate::entity;
|
||||
|
||||
/// The database representation of a watched movie
|
||||
/// The database representation of a watched movie.
|
||||
#[sea_orm::model]
|
||||
#[derive(Debug, Clone, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "flix_watched_collections")]
|
||||
pub struct Model {
|
||||
/// The collection's ID
|
||||
/// The collection's ID.
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: CollectionId,
|
||||
/// The user's ID
|
||||
/// The user's ID.
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub user_id: RawId,
|
||||
/// The date this collection was watched
|
||||
/// The date this collection was watched.
|
||||
pub watched_date: DateTime<Utc>,
|
||||
|
||||
/// The info for this collection
|
||||
/// The info for this collection.
|
||||
#[sea_orm(
|
||||
belongs_to,
|
||||
relation_enum = "Info",
|
||||
@@ -33,15 +33,16 @@ pub mod collections {
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
pub info: HasOne<entity::info::collections::Entity>,
|
||||
/// The content for this collection
|
||||
/// The content for this collection.
|
||||
#[sea_orm(belongs_to, relation_enum = "Content", from = "id", to = "id", skip_fk)]
|
||||
pub content: HasOne<entity::content::collections::Entity>,
|
||||
}
|
||||
|
||||
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
}
|
||||
|
||||
/// Movie entity
|
||||
/// Movie entity.
|
||||
pub mod movies {
|
||||
use flix_model::id::{MovieId, RawId};
|
||||
|
||||
@@ -50,21 +51,21 @@ pub mod movies {
|
||||
|
||||
use crate::entity;
|
||||
|
||||
/// The database representation of a watched movie
|
||||
/// The database representation of a watched movie.
|
||||
#[sea_orm::model]
|
||||
#[derive(Debug, Clone, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "flix_watched_movies")]
|
||||
pub struct Model {
|
||||
/// The movie's ID
|
||||
/// The movie's ID.
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: MovieId,
|
||||
/// The user's ID
|
||||
/// The user's ID.
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub user_id: RawId,
|
||||
/// The date this movie was watched
|
||||
/// The date this movie was watched.
|
||||
pub watched_date: DateTime<Utc>,
|
||||
|
||||
/// The info for this movie
|
||||
/// The info for this movie.
|
||||
#[sea_orm(
|
||||
belongs_to,
|
||||
from = "id",
|
||||
@@ -73,15 +74,16 @@ pub mod movies {
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
pub info: HasOne<entity::info::movies::Entity>,
|
||||
/// The content for this movie
|
||||
/// The content for this movie.
|
||||
#[sea_orm(belongs_to, relation_enum = "Content", from = "id", to = "id", skip_fk)]
|
||||
pub content: HasOne<entity::content::movies::Entity>,
|
||||
}
|
||||
|
||||
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
}
|
||||
|
||||
/// Show entity
|
||||
/// Show entity.
|
||||
pub mod shows {
|
||||
use flix_model::id::{RawId, ShowId};
|
||||
|
||||
@@ -90,21 +92,21 @@ pub mod shows {
|
||||
|
||||
use crate::entity;
|
||||
|
||||
/// The database representation of a watched movie
|
||||
/// The database representation of a watched movie.
|
||||
#[sea_orm::model]
|
||||
#[derive(Debug, Clone, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "flix_watched_shows")]
|
||||
pub struct Model {
|
||||
/// The show's ID
|
||||
/// The show's ID.
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: ShowId,
|
||||
/// The user's ID
|
||||
/// The user's ID.
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub user_id: RawId,
|
||||
/// The date this show was watched
|
||||
/// The date this show was watched.
|
||||
pub watched_date: DateTime<Utc>,
|
||||
|
||||
/// The info for this show
|
||||
/// The info for this show.
|
||||
#[sea_orm(
|
||||
belongs_to,
|
||||
relation_enum = "Info",
|
||||
@@ -114,15 +116,16 @@ pub mod shows {
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
pub info: HasOne<entity::info::shows::Entity>,
|
||||
/// The content for this show
|
||||
/// The content for this show.
|
||||
#[sea_orm(belongs_to, relation_enum = "Content", from = "id", to = "id", skip_fk)]
|
||||
pub content: HasOne<entity::content::shows::Entity>,
|
||||
}
|
||||
|
||||
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
}
|
||||
|
||||
/// Season entity
|
||||
/// Season entity.
|
||||
pub mod seasons {
|
||||
use flix_model::id::{RawId, ShowId};
|
||||
use flix_model::numbers::SeasonNumber;
|
||||
@@ -132,24 +135,24 @@ pub mod seasons {
|
||||
|
||||
use crate::entity;
|
||||
|
||||
/// The database representation of a watched movie
|
||||
/// The database representation of a watched movie.
|
||||
#[sea_orm::model]
|
||||
#[derive(Debug, Clone, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "flix_watched_seasons")]
|
||||
pub struct Model {
|
||||
/// The season's show's ID
|
||||
/// The season's show's ID.
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub show_id: ShowId,
|
||||
/// The season's number
|
||||
/// The season's number.
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub season_number: SeasonNumber,
|
||||
/// The user's ID
|
||||
/// The user's ID.
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub user_id: RawId,
|
||||
/// The date this season was watched
|
||||
/// The date this season was watched.
|
||||
pub watched_date: DateTime<Utc>,
|
||||
|
||||
/// The info for this season
|
||||
/// The info for this season.
|
||||
#[sea_orm(
|
||||
belongs_to,
|
||||
relation_enum = "Info",
|
||||
@@ -159,7 +162,7 @@ pub mod seasons {
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
pub info: HasOne<entity::info::seasons::Entity>,
|
||||
/// The content for this season
|
||||
/// The content for this season.
|
||||
#[sea_orm(
|
||||
belongs_to,
|
||||
relation_enum = "Content",
|
||||
@@ -170,10 +173,11 @@ pub mod seasons {
|
||||
pub content: HasOne<entity::content::seasons::Entity>,
|
||||
}
|
||||
|
||||
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
}
|
||||
|
||||
/// Episode entity
|
||||
/// Episode entity.
|
||||
pub mod episodes {
|
||||
use flix_model::id::{RawId, ShowId};
|
||||
use flix_model::numbers::{EpisodeNumber, SeasonNumber};
|
||||
@@ -183,27 +187,27 @@ pub mod episodes {
|
||||
|
||||
use crate::entity;
|
||||
|
||||
/// The database representation of a watched movie
|
||||
/// The database representation of a watched movie.
|
||||
#[sea_orm::model]
|
||||
#[derive(Debug, Clone, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "flix_watched_episodes")]
|
||||
pub struct Model {
|
||||
/// The episode's show's ID
|
||||
/// The episode's show's ID.
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub show_id: ShowId,
|
||||
/// The episode's season's number
|
||||
/// The episode's season's number.
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub season_number: SeasonNumber,
|
||||
/// The episode's number
|
||||
/// The episode's number.
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub episode_number: EpisodeNumber,
|
||||
/// The user's ID
|
||||
/// The user's ID.
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub user_id: RawId,
|
||||
/// The date this episode was watched
|
||||
/// The date this episode was watched.
|
||||
pub watched_date: DateTime<Utc>,
|
||||
|
||||
/// The info for this episode
|
||||
/// The info for this episode.
|
||||
#[sea_orm(
|
||||
belongs_to,
|
||||
relation_enum = "Info",
|
||||
@@ -213,7 +217,7 @@ pub mod episodes {
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
pub info: HasOne<entity::info::episodes::Entity>,
|
||||
/// The content for this episode
|
||||
/// The content for this episode.
|
||||
#[sea_orm(
|
||||
belongs_to,
|
||||
relation_enum = "Content",
|
||||
@@ -224,15 +228,16 @@ pub mod episodes {
|
||||
pub content: HasOne<entity::content::episodes::Entity>,
|
||||
}
|
||||
|
||||
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
}
|
||||
|
||||
/// Macros for creating watched entities
|
||||
/// Macros for creating watched entities.
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
macro_rules! make_watched_movie {
|
||||
($db:expr, $id:expr, $user:expr) => {
|
||||
$crate::entity::watched::movies::ActiveModel {
|
||||
_ = $crate::entity::watched::movies::ActiveModel {
|
||||
id: Set(::flix_model::id::MovieId::from_raw($id)),
|
||||
user_id: Set($user),
|
||||
watched_date: Set(::chrono::Utc::now()),
|
||||
@@ -246,7 +251,7 @@ pub mod test {
|
||||
|
||||
macro_rules! make_watched_episode {
|
||||
($db:expr, $show:expr, $season:expr, $episode:expr, $user:expr) => {
|
||||
$crate::entity::watched::episodes::ActiveModel {
|
||||
_ = $crate::entity::watched::episodes::ActiveModel {
|
||||
show_id: Set(::flix_model::id::ShowId::from_raw($show)),
|
||||
season_number: Set(::flix_model::numbers::SeasonNumber::new($season)),
|
||||
episode_number: Set(::flix_model::numbers::EpisodeNumber::new($episode)),
|
||||
@@ -284,7 +289,10 @@ mod tests {
|
||||
use super::super::tests::notsettable;
|
||||
use super::test::{make_watched_episode, make_watched_movie};
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[tokio::test]
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
#[expect(clippy::default_numeric_fallback, reason = "unit test")]
|
||||
async fn use_test_macros() {
|
||||
let db = new_initialized_memory_db().await;
|
||||
|
||||
@@ -297,8 +305,11 @@ mod tests {
|
||||
make_watched_episode!(&db, 1, 1, 1, 1);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[tokio::test]
|
||||
async fn test_round_trip_movies() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
#[expect(clippy::default_numeric_fallback, reason = "unit test")]
|
||||
async fn round_trip_movies() {
|
||||
let db = new_initialized_memory_db().await;
|
||||
|
||||
macro_rules! assert_movie {
|
||||
@@ -340,8 +351,11 @@ mod tests {
|
||||
assert_movie!(&db, 5, 1, NotNullViolation; watched_date);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[tokio::test]
|
||||
async fn test_round_trip_episodes() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
#[expect(clippy::default_numeric_fallback, reason = "unit test")]
|
||||
async fn round_trip_episodes() {
|
||||
let db = new_initialized_memory_db().await;
|
||||
|
||||
macro_rules! assert_episode {
|
||||
@@ -394,13 +408,16 @@ mod tests {
|
||||
assert_episode!(&db, 7, 1, 1, 1, NotNullViolation; watched_date);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[tokio::test]
|
||||
async fn test_query_seasons() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
#[expect(clippy::default_numeric_fallback, reason = "unit test")]
|
||||
async fn query_seasons() {
|
||||
let db = new_initialized_memory_db().await;
|
||||
|
||||
macro_rules! assert_season {
|
||||
($db:expr, $show:literal, $season:literal, $uid:literal, Watched) => {
|
||||
assert_season!(@find, $db, $show, $season, $uid)
|
||||
_ = assert_season!(@find, $db, $show, $season, $uid)
|
||||
.ok_or(())
|
||||
.expect("is none");
|
||||
};
|
||||
@@ -462,13 +479,16 @@ mod tests {
|
||||
assert_season!(&db, 1, 2, 3, Unwatched);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[tokio::test]
|
||||
async fn test_query_shows() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
#[expect(clippy::default_numeric_fallback, reason = "unit test")]
|
||||
async fn query_shows() {
|
||||
let db = new_initialized_memory_db().await;
|
||||
|
||||
macro_rules! assert_show {
|
||||
($db:expr, $show:literal, $uid:literal, Watched) => {
|
||||
assert_show!(@find, $db, $show, $uid)
|
||||
_ = assert_show!(@find, $db, $show, $uid)
|
||||
.ok_or(())
|
||||
.expect("is none");
|
||||
};
|
||||
@@ -517,13 +537,16 @@ mod tests {
|
||||
assert_show!(&db, 1, 2, Unwatched);
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
#[tokio::test]
|
||||
async fn test_query_collections() {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
#[expect(clippy::default_numeric_fallback, reason = "unit test")]
|
||||
async fn query_collections() {
|
||||
let db = new_initialized_memory_db().await;
|
||||
|
||||
macro_rules! assert_collection {
|
||||
($db:expr, $id:literal, $uid:literal, Watched) => {
|
||||
assert_collection!(@find, $db, $id, $uid)
|
||||
_ = assert_collection!(@find, $db, $id, $uid)
|
||||
.ok_or(())
|
||||
.expect("is none");
|
||||
};
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
//! flix-db provides types for storing persistent data about media
|
||||
//! flix-db provides types for storing persistent data about media.
|
||||
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate alloc;
|
||||
|
||||
pub mod connection;
|
||||
pub mod entity;
|
||||
pub mod migration;
|
||||
@@ -12,7 +15,8 @@ mod tests {
|
||||
|
||||
use crate::connection::Connection;
|
||||
|
||||
pub async fn new_initialized_memory_db() -> DatabaseConnection {
|
||||
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||
pub(crate) async fn new_initialized_memory_db() -> DatabaseConnection {
|
||||
let options = ConnectOptions::new("sqlite::memory:");
|
||||
|
||||
let db = Database::connect(options)
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
//! Custom views for collections.
|
||||
|
||||
use sea_orm::prelude::*;
|
||||
use sea_orm::sea_query::Table;
|
||||
use sea_orm::{ConnectionTrait, DbBackend, Statement};
|
||||
use sea_orm::{DbBackend, Statement};
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
pub async fn up(manager: &SchemaManager<'_>) -> Result<(), DbErr> {
|
||||
/// # Errors
|
||||
/// Database operations can fail.
|
||||
pub(crate) async fn up(manager: &SchemaManager<'_>) -> Result<(), DbErr> {
|
||||
manager
|
||||
.drop_table(Table::drop().table("flix_watched_collections").to_owned())
|
||||
.await?;
|
||||
|
||||
manager
|
||||
_ = manager
|
||||
.get_connection()
|
||||
.execute_raw(Statement::from_string(
|
||||
DbBackend::Sqlite,
|
||||
r#"
|
||||
"
|
||||
CREATE VIEW flix_watched_collections AS
|
||||
WITH RECURSIVE
|
||||
watched_items AS (
|
||||
@@ -81,22 +85,24 @@ pub async fn up(manager: &SchemaManager<'_>) -> Result<(), DbErr> {
|
||||
)
|
||||
GROUP BY ci.parent_id, wi.user_id
|
||||
;
|
||||
"#,
|
||||
",
|
||||
))
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn down(manager: &SchemaManager<'_>) -> Result<(), DbErr> {
|
||||
manager
|
||||
/// # Errors
|
||||
/// Database operations can fail.
|
||||
pub(crate) async fn down(manager: &SchemaManager<'_>) -> Result<(), DbErr> {
|
||||
_ = manager
|
||||
.get_connection()
|
||||
.execute_raw(Statement::from_string(
|
||||
DbBackend::Sqlite,
|
||||
r#"
|
||||
"
|
||||
DROP VIEW flix_watched_collections
|
||||
;
|
||||
"#,
|
||||
",
|
||||
))
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -11,10 +11,16 @@ mod collections;
|
||||
mod seasons;
|
||||
mod shows;
|
||||
|
||||
/// The migration entry point.
|
||||
#[derive(DeriveMigrationName)]
|
||||
pub(super) struct Migration;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
#[expect(
|
||||
elided_lifetimes_in_paths,
|
||||
reason = "async_trait causes lifetimes to be strange"
|
||||
)]
|
||||
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||
impl MigrationTrait for Migration {
|
||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
seasons::up(manager).await?;
|
||||
@@ -1,18 +1,22 @@
|
||||
//! Custom views for seasons.
|
||||
|
||||
use sea_orm::prelude::*;
|
||||
use sea_orm::sea_query::Table;
|
||||
use sea_orm::{ConnectionTrait, DbBackend, Statement};
|
||||
use sea_orm::{DbBackend, Statement};
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
pub async fn up(manager: &SchemaManager<'_>) -> Result<(), DbErr> {
|
||||
/// # Errors
|
||||
/// Database operations can fail.
|
||||
pub(crate) async fn up(manager: &SchemaManager<'_>) -> Result<(), DbErr> {
|
||||
manager
|
||||
.drop_table(Table::drop().table("flix_watched_seasons").to_owned())
|
||||
.await?;
|
||||
|
||||
manager
|
||||
_ = manager
|
||||
.get_connection()
|
||||
.execute_raw(Statement::from_string(
|
||||
DbBackend::Sqlite,
|
||||
r#"
|
||||
"
|
||||
CREATE VIEW flix_watched_seasons AS
|
||||
SELECT
|
||||
w.show_id,
|
||||
@@ -36,22 +40,24 @@ pub async fn up(manager: &SchemaManager<'_>) -> Result<(), DbErr> {
|
||||
)
|
||||
GROUP BY w.show_id, w.season_number, w.user_id
|
||||
;
|
||||
"#,
|
||||
",
|
||||
))
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn down(manager: &SchemaManager<'_>) -> Result<(), DbErr> {
|
||||
manager
|
||||
/// # Errors
|
||||
/// Database operations can fail.
|
||||
pub(crate) async fn down(manager: &SchemaManager<'_>) -> Result<(), DbErr> {
|
||||
_ = manager
|
||||
.get_connection()
|
||||
.execute_raw(Statement::from_string(
|
||||
DbBackend::Sqlite,
|
||||
r#"
|
||||
"
|
||||
DROP VIEW flix_watched_seasons
|
||||
;
|
||||
"#,
|
||||
",
|
||||
))
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
//! Custom views for shows.
|
||||
|
||||
use sea_orm::prelude::*;
|
||||
use sea_orm::sea_query::Table;
|
||||
use sea_orm::{ConnectionTrait, DbBackend, Statement};
|
||||
use sea_orm::{DbBackend, Statement};
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
pub async fn up(manager: &SchemaManager<'_>) -> Result<(), DbErr> {
|
||||
/// # Errors
|
||||
/// Database operations can fail.
|
||||
pub(crate) async fn up(manager: &SchemaManager<'_>) -> Result<(), DbErr> {
|
||||
manager
|
||||
.drop_table(Table::drop().table("flix_watched_shows").to_owned())
|
||||
.await?;
|
||||
|
||||
manager
|
||||
_ = manager
|
||||
.get_connection()
|
||||
.execute_raw(Statement::from_string(
|
||||
DbBackend::Sqlite,
|
||||
r#"
|
||||
"
|
||||
CREATE VIEW flix_watched_shows AS
|
||||
SELECT
|
||||
w.show_id as id,
|
||||
@@ -33,22 +37,24 @@ pub async fn up(manager: &SchemaManager<'_>) -> Result<(), DbErr> {
|
||||
)
|
||||
GROUP BY w.show_id, w.user_id
|
||||
;
|
||||
"#,
|
||||
",
|
||||
))
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn down(manager: &SchemaManager<'_>) -> Result<(), DbErr> {
|
||||
manager
|
||||
/// # Errors
|
||||
/// Database operations can fail.
|
||||
pub(crate) async fn down(manager: &SchemaManager<'_>) -> Result<(), DbErr> {
|
||||
_ = manager
|
||||
.get_connection()
|
||||
.execute_raw(Statement::from_string(
|
||||
DbBackend::Sqlite,
|
||||
r#"
|
||||
"
|
||||
DROP VIEW flix_watched_shows
|
||||
;
|
||||
"#,
|
||||
",
|
||||
))
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Migrations for maintaining the database schema
|
||||
//! Migrations for maintaining the database schema.
|
||||
|
||||
seamantic::migrations! {
|
||||
"seaql_migrations_flix";
|
||||
|
||||
Reference in New Issue
Block a user