diff --git a/src/controllers/krate/metadata.rs b/src/controllers/krate/metadata.rs index ae0a652c77e..0138ce419d6 100644 --- a/src/controllers/krate/metadata.rs +++ b/src/controllers/krate/metadata.rs @@ -11,7 +11,6 @@ use crate::models::{ Version, VersionOwnerAction, }; use crate::schema::*; -use crate::tasks::spawn_blocking; use crate::util::diesel::prelude::*; use crate::util::errors::{bad_request, crate_not_found, AppResult, BoxedAppError}; use crate::util::{redirect, RequestUtils}; @@ -22,7 +21,6 @@ use axum::extract::Path; use axum::response::{IntoResponse, Response}; use axum_extra::json; use axum_extra::response::ErasedJson; -use diesel_async::async_connection_wrapper::AsyncConnectionWrapper; use http::request::Parts; use std::cmp::Reverse; use std::str::FromStr; @@ -34,25 +32,19 @@ pub async fn show_new(app: AppState, req: Parts) -> AppResult { /// Handles the `GET /crates/:crate_id` route. pub async fn show(app: AppState, Path(name): Path, req: Parts) -> AppResult { - let conn = app.db_read().await?; - spawn_blocking(move || { - use diesel::RunQueryDsl; - - let conn: &mut AsyncConnectionWrapper<_> = &mut conn.into(); - - let include = req - .query() - .get("include") - .map(|mode| ShowIncludeMode::from_str(mode)) - .transpose()? - .unwrap_or_default(); - - let (krate, downloads, default_version, yanked): ( - Crate, - i64, - Option, - Option, - ) = Crate::by_name(&name) + use diesel_async::RunQueryDsl; + + let mut conn = app.db_read().await?; + + let include = req + .query() + .get("include") + .map(|mode| ShowIncludeMode::from_str(mode)) + .transpose()? + .unwrap_or_default(); + + let (krate, downloads, default_version, yanked): (Crate, i64, Option, Option) = + Crate::by_name(&name) .inner_join(crate_downloads::table) .left_join(default_versions::table) .left_join(versions::table.on(default_versions::version_id.eq(versions::id))) @@ -62,109 +54,116 @@ pub async fn show(app: AppState, Path(name): Path, req: Parts) -> AppRes versions::num.nullable(), versions::yanked.nullable(), )) - .first(conn) + .first(&mut conn) + .await .optional()? .ok_or_else(|| crate_not_found(&name))?; - let versions_publishers_and_audit_actions = if include.versions { - let mut versions_and_publishers: Vec<(Version, Option)> = - Version::belonging_to(&krate) - .left_outer_join(users::table) - .select(<(Version, Option)>::as_select()) - .load(conn)?; - versions_and_publishers.sort_by_cached_key(|(version, _)| { - Reverse(semver::Version::parse(&version.num).ok()) - }); - - let versions = versions_and_publishers - .iter() - .map(|(v, _)| v) - .collect::>(); - let actions = VersionOwnerAction::for_versions(conn, &versions)?; - Some( - versions_and_publishers - .into_iter() - .zip(actions) - .map(|((v, pb), aas)| (v, pb, aas)) - .collect::>(), - ) - } else { - None - }; - let ids = versions_publishers_and_audit_actions - .as_ref() - .map(|vps| vps.iter().map(|v| v.0.id).collect()); - - let kws = if include.keywords { - Some( - CrateKeyword::belonging_to(&krate) - .inner_join(keywords::table) - .select(Keyword::as_select()) - .load(conn)?, - ) - } else { - None - }; - let cats = if include.categories { - Some( - CrateCategory::belonging_to(&krate) - .inner_join(categories::table) - .select(Category::as_select()) - .load(conn)?, - ) - } else { - None - }; - let recent_downloads = if include.downloads { - RecentCrateDownloads::belonging_to(&krate) - .select(recent_crate_downloads::downloads) - .get_result(conn) - .optional()? - } else { - None - }; + let versions_publishers_and_audit_actions = if include.versions { + let mut versions_and_publishers: Vec<(Version, Option)> = + Version::belonging_to(&krate) + .left_outer_join(users::table) + .select(<(Version, Option)>::as_select()) + .load(&mut conn) + .await?; - let top_versions = if include.versions { - Some(krate.top_versions(conn)?) - } else { - None - }; + versions_and_publishers + .sort_by_cached_key(|(version, _)| Reverse(semver::Version::parse(&version.num).ok())); - let encodable_crate = EncodableCrate::from( - krate.clone(), - default_version.as_deref(), - yanked, - top_versions.as_ref(), - ids, - kws.as_deref(), - cats.as_deref(), - false, - downloads, - recent_downloads, - ); - let encodable_versions = versions_publishers_and_audit_actions.map(|vpa| { - vpa.into_iter() - .map(|(v, pb, aas)| EncodableVersion::from(v, &krate.name, pb, aas)) - .collect::>() - }); - let encodable_keywords = kws.map(|kws| { - kws.into_iter() - .map(Keyword::into) - .collect::>() - }); - let encodable_cats = cats.map(|cats| { - cats.into_iter() - .map(Category::into) - .collect::>() - }); - Ok(json!({ - "crate": encodable_crate, - "versions": encodable_versions, - "keywords": encodable_keywords, - "categories": encodable_cats, - })) - }) - .await? + let versions = versions_and_publishers + .iter() + .map(|(v, _)| v) + .collect::>(); + let actions = VersionOwnerAction::async_for_versions(&mut conn, &versions).await?; + Some( + versions_and_publishers + .into_iter() + .zip(actions) + .map(|((v, pb), aas)| (v, pb, aas)) + .collect::>(), + ) + } else { + None + }; + let ids = versions_publishers_and_audit_actions + .as_ref() + .map(|vps| vps.iter().map(|v| v.0.id).collect()); + + let kws = if include.keywords { + Some( + CrateKeyword::belonging_to(&krate) + .inner_join(keywords::table) + .select(Keyword::as_select()) + .load(&mut conn) + .await?, + ) + } else { + None + }; + let cats = if include.categories { + Some( + CrateCategory::belonging_to(&krate) + .inner_join(categories::table) + .select(Category::as_select()) + .load(&mut conn) + .await?, + ) + } else { + None + }; + let recent_downloads = if include.downloads { + RecentCrateDownloads::belonging_to(&krate) + .select(recent_crate_downloads::downloads) + .get_result(&mut conn) + .await + .optional()? + } else { + None + }; + + let top_versions = if include.versions { + Some(krate.async_top_versions(&mut conn).await?) + } else { + None + }; + + let encodable_crate = EncodableCrate::from( + krate.clone(), + default_version.as_deref(), + yanked, + top_versions.as_ref(), + ids, + kws.as_deref(), + cats.as_deref(), + false, + downloads, + recent_downloads, + ); + + let encodable_versions = versions_publishers_and_audit_actions.map(|vpa| { + vpa.into_iter() + .map(|(v, pb, aas)| EncodableVersion::from(v, &krate.name, pb, aas)) + .collect::>() + }); + + let encodable_keywords = kws.map(|kws| { + kws.into_iter() + .map(Keyword::into) + .collect::>() + }); + + let encodable_cats = cats.map(|cats| { + cats.into_iter() + .map(Category::into) + .collect::>() + }); + + Ok(json!({ + "crate": encodable_crate, + "versions": encodable_versions, + "keywords": encodable_keywords, + "categories": encodable_cats, + })) } #[derive(Debug)] diff --git a/src/models/krate.rs b/src/models/krate.rs index ecc0f0759d4..dfcafb7bb36 100644 --- a/src/models/krate.rs +++ b/src/models/krate.rs @@ -341,6 +341,24 @@ impl Crate { } } + /// Return both the newest (most recently updated) and + /// highest version (in semver order) for the current crate, + /// where all top versions are not yanked. + pub async fn async_top_versions( + &self, + conn: &mut AsyncPgConnection, + ) -> QueryResult { + use diesel_async::RunQueryDsl; + + Ok(TopVersions::from_date_version_pairs( + Version::belonging_to(self) + .filter(versions::yanked.eq(false)) + .select((versions::created_at, versions::num)) + .load(conn) + .await?, + )) + } + /// Return both the newest (most recently updated) and /// highest version (in semver order) for the current crate, /// where all top versions are not yanked.