use std::sync::Arc; use async_trait::async_trait; use axum::{ extract::{FromRequest, Path as AxumPath, RequestParts}, Extension, }; use semver::Version; use serde::de::DeserializeOwned; use sqlx::query_as; use crate::{ auth, db::PgU32, db_error, index::update_crate_from_db, Auth, Errors, RespResult, State, Success, }; pub struct Path(T); #[async_trait] impl FromRequest for Path where T: DeserializeOwned + Send, B: Send, { type Rejection = Errors; async fn from_request(req: &mut RequestParts) -> Result { let res = AxumPath::::from_request(req).await; match res { Ok(AxumPath(v)) => Ok(Path(v)), Err(e) => Err(Errors::new(format_args!("Invalid path fragments: {e}"))), } } } pub async fn yank( Path((crate_name, version)): Path<(String, Version)>, Auth(auth_user): Auth, Extension(state): Extension>, ) -> RespResult { auth(&crate_name, &auth_user, &state).await?; do_yank(&crate_name, &version, &state, true).await } pub async fn unyank( Path((crate_name, version)): Path<(String, Version)>, Auth(auth_user): Auth, Extension(state): Extension>, ) -> RespResult { auth(&crate_name, &auth_user, &state).await?; do_yank(&crate_name, &version, &state, false).await } async fn do_yank(crate_name: &str, version: &Version, state: &State, yank: bool) -> RespResult { let (crate_id,): (PgU32,) = query_as( r"UPDATE versions SET yanked = $1 FROM crates WHERE crates.id = versions.crate_id and crates.name = $2 and versions.vers = $3 RETURNING crates.id", ) .bind(yank) .bind(crate_name) .bind(&version.to_string()) .fetch_one(&state.db) .await .map_err(db_error)?; update_crate_from_db( crate_id, state, &format!( "{} version {} of `{}`", if yank { "Yank" } else { "Unyank" }, version, crate_name ), ) .await?; Ok(Success) }