|
1 |
| -use std::{collections::HashMap, io, net::SocketAddr, path::PathBuf, process::Stdio}; |
| 1 | +use std::{collections::HashMap, io, net::SocketAddr, path::{Path, PathBuf}, process::Stdio}; |
2 | 2 |
|
3 | 3 | use askama::Template;
|
4 | 4 | use bytes::BytesMut;
|
5 | 5 | use futures_util::stream::TryStreamExt;
|
6 | 6 | use include_dir::{include_dir, Dir};
|
| 7 | +use panamax_search_lib::Index; |
| 8 | +use serde::{Deserialize, Serialize}; |
7 | 9 | use thiserror::Error;
|
8 | 10 | use tokio::{
|
9 | 11 | fs::File,
|
@@ -57,6 +59,56 @@ pub enum ServeError {
|
57 | 59 |
|
58 | 60 | impl Reject for ServeError {}
|
59 | 61 |
|
| 62 | +#[derive(Deserialize)] |
| 63 | +struct CratesSearch { |
| 64 | + q: String, |
| 65 | + per_page: usize, |
| 66 | +} |
| 67 | + |
| 68 | +async fn search(p: &CratesSearch, path: &Path) -> Result<http::Response<String>, Rejection> { |
| 69 | + if let Ok(index) = Index::load(path) { |
| 70 | + let crates = index |
| 71 | + .search(&[p.q.clone()], true) |
| 72 | + .to_vec() |
| 73 | + .into_iter() |
| 74 | + .map(|crate_| Crate { |
| 75 | + name: crate_.name, |
| 76 | + description: crate_.description, |
| 77 | + max_version: if let Some(v) = &crate_.latest_ny { |
| 78 | + v.to_string() |
| 79 | + } else { |
| 80 | + String::from("0.0.0") |
| 81 | + }, |
| 82 | + }) |
| 83 | + .collect::<Vec<_>>(); |
| 84 | + let meta = TotalCrates { total: crates.len() as u32 }; |
| 85 | + let crates = Crates { crates, meta }; |
| 86 | + let body = serde_json::to_string(&crates).unwrap(); |
| 87 | + if let Ok(res) = Response::builder().body(body) { |
| 88 | + return Ok(res); |
| 89 | + } |
| 90 | + } |
| 91 | + Err(warp::reject::not_found()) |
| 92 | +} |
| 93 | + |
| 94 | +#[derive(Serialize)] |
| 95 | +struct Crate { |
| 96 | + name: String, |
| 97 | + description: Option<String>, |
| 98 | + max_version: String, |
| 99 | +} |
| 100 | + |
| 101 | +#[derive(Serialize)] |
| 102 | +struct TotalCrates { |
| 103 | + total: u32, |
| 104 | +} |
| 105 | + |
| 106 | +#[derive(Serialize)] |
| 107 | +struct Crates { |
| 108 | + crates: Vec<Crate>, |
| 109 | + meta: TotalCrates, |
| 110 | +} |
| 111 | + |
60 | 112 | pub async fn serve(path: PathBuf, socket_addr: SocketAddr, tls_paths: Option<TlsConfig>) {
|
61 | 113 | let index_path = path.clone();
|
62 | 114 | let is_tls = tls_paths.is_some();
|
@@ -84,6 +136,15 @@ pub async fn serve(path: PathBuf, socket_addr: SocketAddr, tls_paths: Option<Tls
|
84 | 136 | },
|
85 | 137 | );
|
86 | 138 |
|
| 139 | + // Handle `cargo search` queries ("/crates?q={}&per_page={}") |
| 140 | + let search_path = path.clone(); |
| 141 | + let crates_search = warp::path::path("crates") |
| 142 | + .and(warp::query::<CratesSearch>()) |
| 143 | + .and_then(move |p: CratesSearch| { |
| 144 | + let path = search_path.clone(); |
| 145 | + async move { search(&p, &path).await } |
| 146 | + }); |
| 147 | + |
87 | 148 | // Handle all files baked into the binary with include_dir, at /static
|
88 | 149 | let static_dir =
|
89 | 150 | warp::path::path("static")
|
@@ -190,6 +251,7 @@ pub async fn serve(path: PathBuf, socket_addr: SocketAddr, tls_paths: Option<Tls
|
190 | 251 | .or(static_dir)
|
191 | 252 | .or(dist_dir)
|
192 | 253 | .or(rustup_dir)
|
| 254 | + .or(crates_search) |
193 | 255 | .or(crates_dir_native_format)
|
194 | 256 | .or(crates_dir_condensed_format)
|
195 | 257 | .or(sparse_index)
|
|
0 commit comments