diff --git a/src/dto_helpers.rs b/src/dto_helpers.rs new file mode 100644 index 0000000000000000000000000000000000000000..915376cbe86986798edd77298ab929acbfea84f7 --- /dev/null +++ b/src/dto_helpers.rs @@ -0,0 +1,110 @@ +use diesel::*; + +use crate::dto::*; +use crate::models::*; +use crate::schema::*; + +pub fn load_title(db: &PgConnection, title: Title) -> QueryResult<TitleDto> { + let title_names: Vec<TitleName> = TitleName::belonging_to(&title) + .load::<TitleName>(db)?; + let title_descriptions: Vec<TitleDescription> = TitleDescription::belonging_to(&title) + .load::<TitleDescription>(db)?; + let title_cast: Vec<(TitleCast, Person)> = TitleCast::belonging_to(&title) + .inner_join(people::table) + .load::<(TitleCast, Person)>(db)?; + let title_genres: Vec<(TitleGenre, Genre)> = TitleGenre::belonging_to(&title) + .inner_join(genres::table) + .load::<(TitleGenre, Genre)>(db)?; + let title_ratings: Vec<TitleRating> = TitleRating::belonging_to(&title) + .load::<TitleRating>(db)?; + let title_images: Vec<TitleImage> = TitleImage::belonging_to(&title) + .load::<TitleImage>(db)?; + let title_media: Vec<TitleMedium> = TitleMedium::belonging_to(&title) + .load::<TitleMedium>(db)?; + let title_subtitles: Vec<TitleSubtitle> = TitleSubtitle::belonging_to(&title) + .load::<TitleSubtitle>(db)?; + Ok(process_title( + title, title_names, title_descriptions, + title_cast, title_genres, title_ratings, + title_images, title_media, title_subtitles, + )) +} + +pub fn load_titles(db: &PgConnection, titles: Vec<Title>) -> QueryResult<Vec<TitleDto>> { + let title_names: Vec<Vec<TitleName>> = TitleName::belonging_to(&titles) + .load::<TitleName>(db)? + .grouped_by(&titles); + let title_descriptions: Vec<Vec<TitleDescription>> = TitleDescription::belonging_to(&titles) + .load::<TitleDescription>(db)? + .grouped_by(&titles); + let title_cast: Vec<Vec<(TitleCast, Person)>> = TitleCast::belonging_to(&titles) + .inner_join(people::table) + .load::<(TitleCast, Person)>(db)? + .grouped_by(&titles); + let title_genres: Vec<Vec<(TitleGenre, Genre)>> = TitleGenre::belonging_to(&titles) + .inner_join(genres::table) + .load::<(TitleGenre, Genre)>(db)? + .grouped_by(&titles); + let title_ratings: Vec<Vec<TitleRating>> = TitleRating::belonging_to(&titles) + .load::<TitleRating>(db)? + .grouped_by(&titles); + let title_images: Vec<Vec<TitleImage>> = TitleImage::belonging_to(&titles) + .load::<TitleImage>(db)? + .grouped_by(&titles); + let title_media: Vec<Vec<TitleMedium>> = TitleMedium::belonging_to(&titles) + .load::<TitleMedium>(db)? + .grouped_by(&titles); + let title_subtitles: Vec<Vec<TitleSubtitle>> = TitleSubtitle::belonging_to(&titles) + .load::<TitleSubtitle>(db)? + .grouped_by(&titles); + Ok(titles.into_iter() + .zip(title_names) + .zip(title_descriptions) + .zip(title_cast) + .zip(title_genres) + .zip(title_ratings) + .zip(title_images) + .zip(title_media) + .zip(title_subtitles) + .map(|tuple| { + let ((((((((title, akas), descriptions), + cast), genres), ratings), + images), media), subtitles) = tuple; + process_title(title, akas, descriptions, cast, genres, ratings, images, media, subtitles) + }).collect::<Vec<TitleDto>>()) +} + +fn process_title( + title: Title, names: Vec<TitleName>, descriptions: Vec<TitleDescription>, + cast: Vec<(TitleCast, Person)>, genres: Vec<(TitleGenre, Genre)>, ratings: Vec<TitleRating>, + images: Vec<TitleImage>, media: Vec<TitleMedium>, subtitles: Vec<TitleSubtitle> +) -> TitleDto { + TitleDto::of( + title, + names.into_iter().map(|src| { + TitleNameDto::from(src) + }).collect::<Vec<_>>(), + descriptions.into_iter().map(|src| { + TitleDescriptionDto::from(src) + }).collect::<Vec<_>>(), + cast.into_iter().map(|src| { + let (cast, person) = src; + TitleCastDto::of(cast, person) + }).collect::<Vec<_>>(), + genres.into_iter().map(|(_, src)| { + GenreDto::from(src) + }).collect::<Vec<_>>(), + ratings.into_iter().map(|src| { + TitleRatingDto::from(src) + }).collect::<Vec<_>>(), + images.into_iter().map(|src| { + TitleImageDto::from(src) + }).collect::<Vec<_>>(), + media.into_iter().map(|src| { + TitleMediaDto::from(src) + }).collect::<Vec<_>>(), + subtitles.into_iter().map(|src| { + TitleSubtitleDto::from(src) + }).collect::<Vec<_>>(), + ) +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index aab3a82d6f448849444cc00ce150d2ec49393620..51672dc16a88d820a3a533d5ad0e93eab71171ec 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,4 +4,6 @@ extern crate dotenv; pub mod schema; pub mod models; -pub mod dto; \ No newline at end of file +pub mod dto; +pub mod dto_helpers; +pub mod param_helpers; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 4440c1935950f894c69ad8c11f3720644c9009f8..56dd192028c32b3c0c13d392f83e2a0bf49a4e75 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,19 +8,17 @@ extern crate rocket_contrib; use diesel::prelude::*; use dotenv::dotenv; -use rocket::http::RawStr; -use rocket::request::FromParam; use rocket_contrib::databases::diesel; use rocket_contrib::json::Json; -use uuid::Uuid; -use media_backend::dto::{GenreDto, GenreWithTitlesDto, TitleCastDto, TitleDescriptionDto, TitleDto, TitleImageDto, TitleMediaDto, TitleNameDto, TitleRatingDto, TitleSubtitleDto}; +use media_backend::dto::*; +use media_backend::dto_helpers::{load_title, load_titles}; use media_backend::models::*; +use media_backend::param_helpers::ParamUuid; #[database("mediaflix")] struct MediaflixConnection(diesel::PgConnection); - #[get("/api/v1/genres")] fn list_genres(db: MediaflixConnection) -> QueryResult<Json<Vec<Genre>>> { use media_backend::schema::*; @@ -28,109 +26,41 @@ fn list_genres(db: MediaflixConnection) -> QueryResult<Json<Vec<Genre>>> { Ok(Json(query.load::<Genre>(&db.0)?)) } -struct ParamUuid(uuid::Uuid); - -impl FromParam<'_> for ParamUuid { - type Error = uuid::parser::ParseError; - - fn from_param(param: &RawStr) -> Result<Self, Self::Error> { - return Ok(ParamUuid(Uuid::parse_str(param.as_str())?)); - } -} - - #[get("/api/v1/genres/<id>")] fn get_genre(db: MediaflixConnection, id: ParamUuid) -> QueryResult<Json<GenreWithTitlesDto>> { use media_backend::schema::*; let genre: Genre = genres::table - .find(id.0) + .find(id.uuid()) .first::<Genre>(&db.0)?; let titles: Vec<Title> = title_genres::table .filter(title_genres::genre_id.eq(genre.id)) .inner_join(titles::table) .select(titles::all_columns) .load::<Title>(&db.0)?; - let title_names: Vec<Vec<TitleName>> = TitleName::belonging_to(&titles) - .load::<TitleName>(&db.0)? - .grouped_by(&titles); - let title_descriptions: Vec<Vec<TitleDescription>> = TitleDescription::belonging_to(&titles) - .load::<TitleDescription>(&db.0)? - .grouped_by(&titles); - let title_cast: Vec<Vec<(TitleCast, Person)>> = TitleCast::belonging_to(&titles) - .inner_join(people::table) - .load::<(TitleCast, Person)>(&db.0)? - .grouped_by(&titles); - let title_genres: Vec<Vec<(TitleGenre, Genre)>> = TitleGenre::belonging_to(&titles) - .inner_join(genres::table) - .load::<(TitleGenre, Genre)>(&db.0)? - .grouped_by(&titles); - let title_ratings: Vec<Vec<TitleRating>> = TitleRating::belonging_to(&titles) - .load::<TitleRating>(&db.0)? - .grouped_by(&titles); - let title_images: Vec<Vec<TitleImage>> = TitleImage::belonging_to(&titles) - .load::<TitleImage>(&db.0)? - .grouped_by(&titles); - let title_media: Vec<Vec<TitleMedium>> = TitleMedium::belonging_to(&titles) - .load::<TitleMedium>(&db.0)? - .grouped_by(&titles); - let title_subtitles: Vec<Vec<TitleSubtitle>> = TitleSubtitle::belonging_to(&titles) - .load::<TitleSubtitle>(&db.0)? - .grouped_by(&titles); Ok(Json(GenreWithTitlesDto { genre: GenreDto::from(genre), - titles: titles.into_iter() - .zip(title_names) - .zip(title_descriptions) - .zip(title_cast) - .zip(title_genres) - .zip(title_ratings) - .zip(title_images) - .zip(title_media) - .zip(title_subtitles) - .map(|tuple| { - let ((((((((title, akas), descriptions), - cast), genres), ratings), - images), media), subtitles) = tuple; - TitleDto::of( - title, - akas.into_iter().map(|src| { - TitleNameDto::from(src) - }).collect::<Vec<_>>(), - descriptions.into_iter().map(|src| { - TitleDescriptionDto::from(src) - }).collect::<Vec<_>>(), - cast.into_iter().map(|src| { - let (cast, person) = src; - TitleCastDto::of(cast, person) - }).collect::<Vec<_>>(), - genres.into_iter().map(|(_, src)| { - GenreDto::from(src) - }).collect::<Vec<_>>(), - ratings.into_iter().map(|src| { - TitleRatingDto::from(src) - }).collect::<Vec<_>>(), - images.into_iter().map(|src| { - TitleImageDto::from(src) - }).collect::<Vec<_>>(), - media.into_iter().map(|src| { - TitleMediaDto::from(src) - }).collect::<Vec<_>>(), - subtitles.into_iter().map(|src| { - TitleSubtitleDto::from(src) - }).collect::<Vec<_>>(), - ) - }).collect::<Vec<TitleDto>>(), + titles: load_titles(&db.0, titles)?, })) } +#[get("/api/v1/titles")] +fn list_titles(db: MediaflixConnection) -> QueryResult<Json<Vec<TitleDto>>> { + use media_backend::schema::*; + let titles = titles::table + .load::<Title>(&db.0)?; + Ok(Json(load_titles(&db.0, titles)?)) +} #[get("/api/v1/titles/<title>")] -fn get_title(db: MediaflixConnection, title: ParamUuid) -> QueryResult<Json<Title>> { +fn get_title(db: MediaflixConnection, title: ParamUuid) -> QueryResult<Json<TitleDto>> { use media_backend::schema::*; - let query = titles::table - .find(title.0) - .into_boxed(); - Ok(Json(query.first::<Title>(&db.0)?)) + let title = load_title( + &db.0, + titles::table + .find(title.uuid()) + .first::<Title>(&db.0)?, + )?; + Ok(Json(title)) } fn main() { @@ -143,6 +73,7 @@ fn main() { rocket::routes![ list_genres, get_genre, + list_titles, get_title ], ) diff --git a/src/param_helpers.rs b/src/param_helpers.rs new file mode 100644 index 0000000000000000000000000000000000000000..324100e09816d8f4b7310f6a7a57aec81d2649ba --- /dev/null +++ b/src/param_helpers.rs @@ -0,0 +1,19 @@ +use rocket::request::FromParam; +use rocket::http::RawStr; +use uuid::Uuid; + +pub struct ParamUuid(uuid::Uuid); + +impl ParamUuid { + pub fn uuid(&self) -> uuid::Uuid { + self.0 + } +} + +impl FromParam<'_> for ParamUuid { + type Error = uuid::parser::ParseError; + + fn from_param(param: &RawStr) -> Result<Self, Self::Error> { + return Ok(ParamUuid(Uuid::parse_str(param.as_str())?)); + } +} \ No newline at end of file