warehouse/src/yank.rs
2022-08-02 12:33:50 -05:00

87 lines
2.1 KiB
Rust

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>(T);
#[async_trait]
impl<T, B> FromRequest<B> for Path<T>
where
T: DeserializeOwned + Send,
B: Send,
{
type Rejection = Errors;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
let res = AxumPath::<T>::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<Arc<State>>,
) -> 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<Arc<State>>,
) -> 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)
}