//! Does a simple search over the crates table for a search term, the organisation and crate name //! are concatenated using a `/` so any substring of `org/crate` will return results. The latest //! version for each is also fetched so we can show them in the search results. use axum::{extract, Json}; use chartered_db::{crates::Crate, permissions::UserPermission, users::User, ConnectionPool}; use serde::{Deserialize, Serialize}; use std::sync::Arc; use thiserror::Error; pub async fn handle( extract::Extension(db): extract::Extension, extract::Extension(user): extract::Extension>, extract::Query(req): extract::Query, ) -> Result, Error> { let crates = futures::future::try_join_all( Crate::search(db.clone(), user.id, req.q, 5) .await? .into_iter() .flat_map(move |(org, crates_with_permissions)| { let db = db.clone(); crates_with_permissions .into_iter() .map(Arc::new) .map(move |v| { let db = db.clone(); let org_name = org.name.clone(); async move { let version = v.clone().latest_version(db).await?; Ok::<_, Error>(ResponseCrate { organisation: org_name, name: v.crate_.name.clone(), description: v.crate_.description.clone(), version: version.map(|v| v.version).unwrap_or_default(), homepage: v.crate_.homepage.clone(), repository: v.crate_.repository.clone(), permissions: v.permissions, }) } }) }), ) .await?; Ok(Json(Response { crates })) } #[derive(Deserialize)] pub struct RequestParams { q: String, } #[derive(Serialize)] pub struct Response { crates: Vec, } #[derive(Serialize)] pub struct ResponseCrate { organisation: String, name: String, description: Option, version: String, homepage: Option, repository: Option, permissions: UserPermission, } #[derive(Error, Debug)] pub enum Error { #[error("{0}")] Database(#[from] chartered_db::Error), } impl Error { pub fn status_code(&self) -> axum::http::StatusCode { use axum::http::StatusCode; match self { Self::Database(_) => StatusCode::INTERNAL_SERVER_ERROR, } } } define_error_response!(Error);