feat: migrate server to actix-http

This commit is contained in:
Rob Ede 2024-05-26 20:08:54 +01:00
parent ebec04d2de
commit f6ba95c070
No known key found for this signature in database
GPG key ID: 97C636207D3EF933
24 changed files with 394 additions and 274 deletions

3
.rustfmt.toml Normal file
View file

@ -0,0 +1,3 @@
imports_granularity = "Crate"
group_imports = "StdExternalCrate"
use_field_init_shorthand = true

193
Cargo.lock generated
View file

@ -8,6 +8,103 @@ version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046"
[[package]]
name = "actix-codec"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a"
dependencies = [
"bitflags 2.5.0",
"bytes",
"futures-core",
"futures-sink",
"memchr",
"pin-project-lite",
"tokio",
"tokio-util",
"tracing",
]
[[package]]
name = "actix-http"
version = "3.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4eb9843d84c775696c37d9a418bbb01b932629d01870722c0f13eb3f95e2536d"
dependencies = [
"actix-codec",
"actix-rt",
"actix-service",
"actix-utils",
"ahash",
"bitflags 2.5.0",
"bytes",
"bytestring",
"derive_more",
"encoding_rs",
"futures-core",
"h2 0.3.26",
"http 0.2.12",
"httparse",
"httpdate",
"itoa",
"language-tags",
"mime",
"percent-encoding",
"pin-project-lite",
"smallvec",
"tokio",
"tokio-util",
"tracing",
]
[[package]]
name = "actix-rt"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28f32d40287d3f402ae0028a9d54bef51af15c8769492826a69d28f81893151d"
dependencies = [
"futures-core",
"tokio",
]
[[package]]
name = "actix-server"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3eb13e7eef0423ea6eab0e59f6c72e7cb46d33691ad56a726b3cd07ddec2c2d4"
dependencies = [
"actix-rt",
"actix-service",
"actix-utils",
"futures-core",
"futures-util",
"mio",
"socket2",
"tokio",
"tracing",
]
[[package]]
name = "actix-service"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b894941f818cfdc7ccc4b9e60fa7e53b5042a2e8567270f9147d5591893373a"
dependencies = [
"futures-core",
"paste",
"pin-project-lite",
]
[[package]]
name = "actix-utils"
version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8"
dependencies = [
"local-waker",
"pin-project-lite",
]
[[package]] [[package]]
name = "addr2line" name = "addr2line"
version = "0.21.0" version = "0.21.0"
@ -30,6 +127,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"getrandom",
"once_cell", "once_cell",
"version_check", "version_check",
"zerocopy", "zerocopy",
@ -169,6 +267,15 @@ version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
[[package]]
name = "bytestring"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74d80203ea6b29df88012294f62733de21cfeab47f17b41af3a38bc30a03ee72"
dependencies = [
"bytes",
]
[[package]] [[package]]
name = "cadence" name = "cadence"
version = "1.4.0" version = "1.4.0"
@ -1487,17 +1594,6 @@ dependencies = [
"itoa", "itoa",
] ]
[[package]]
name = "http-body"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
dependencies = [
"bytes",
"http 0.2.12",
"pin-project-lite",
]
[[package]] [[package]]
name = "http-body" name = "http-body"
version = "1.0.0" version = "1.0.0"
@ -1517,7 +1613,7 @@ dependencies = [
"bytes", "bytes",
"futures-core", "futures-core",
"http 1.1.0", "http 1.1.0",
"http-body 1.0.0", "http-body",
"pin-project-lite", "pin-project-lite",
] ]
@ -1533,30 +1629,6 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "hyper"
version = "0.14.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80"
dependencies = [
"bytes",
"futures-channel",
"futures-core",
"futures-util",
"h2 0.3.26",
"http 0.2.12",
"http-body 0.4.6",
"httparse",
"httpdate",
"itoa",
"pin-project-lite",
"socket2",
"tokio",
"tower-service",
"tracing",
"want",
]
[[package]] [[package]]
name = "hyper" name = "hyper"
version = "1.3.1" version = "1.3.1"
@ -1568,7 +1640,7 @@ dependencies = [
"futures-util", "futures-util",
"h2 0.4.5", "h2 0.4.5",
"http 1.1.0", "http 1.1.0",
"http-body 1.0.0", "http-body",
"httparse", "httparse",
"itoa", "itoa",
"pin-project-lite", "pin-project-lite",
@ -1585,7 +1657,7 @@ checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c"
dependencies = [ dependencies = [
"futures-util", "futures-util",
"http 1.1.0", "http 1.1.0",
"hyper 1.3.1", "hyper",
"hyper-util", "hyper-util",
"rustls", "rustls",
"rustls-pki-types", "rustls-pki-types",
@ -1602,7 +1674,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
dependencies = [ dependencies = [
"bytes", "bytes",
"http-body-util", "http-body-util",
"hyper 1.3.1", "hyper",
"hyper-util", "hyper-util",
"native-tls", "native-tls",
"tokio", "tokio",
@ -1620,8 +1692,8 @@ dependencies = [
"futures-channel", "futures-channel",
"futures-util", "futures-util",
"http 1.1.0", "http 1.1.0",
"http-body 1.0.0", "http-body",
"hyper 1.3.1", "hyper",
"pin-project-lite", "pin-project-lite",
"socket2", "socket2",
"tokio", "tokio",
@ -1701,6 +1773,12 @@ dependencies = [
"static_assertions", "static_assertions",
] ]
[[package]]
name = "language-tags"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388"
[[package]] [[package]]
name = "lasso" name = "lasso"
version = "0.7.2" version = "0.7.2"
@ -1728,6 +1806,12 @@ version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
[[package]]
name = "local-waker"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487"
[[package]] [[package]]
name = "lock_api" name = "lock_api"
version = "0.4.12" version = "0.4.12"
@ -1829,6 +1913,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
dependencies = [ dependencies = [
"libc", "libc",
"log",
"wasi", "wasi",
"windows-sys 0.48.0", "windows-sys 0.48.0",
] ]
@ -1983,6 +2068,12 @@ dependencies = [
"windows-targets 0.52.5", "windows-targets 0.52.5",
] ]
[[package]]
name = "paste"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]] [[package]]
name = "percent-encoding" name = "percent-encoding"
version = "2.3.1" version = "2.3.1"
@ -2271,9 +2362,9 @@ dependencies = [
"futures-util", "futures-util",
"h2 0.4.5", "h2 0.4.5",
"http 1.1.0", "http 1.1.0",
"http-body 1.0.0", "http-body",
"http-body-util", "http-body-util",
"hyper 1.3.1", "hyper",
"hyper-rustls", "hyper-rustls",
"hyper-tls", "hyper-tls",
"hyper-util", "hyper-util",
@ -2597,6 +2688,9 @@ checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
name = "shiny-robots" name = "shiny-robots"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"actix-http",
"actix-server",
"actix-service",
"anyhow", "anyhow",
"badge", "badge",
"cadence", "cadence",
@ -2607,7 +2701,6 @@ dependencies = [
"futures-util", "futures-util",
"gix", "gix",
"grass", "grass",
"hyper 0.14.28",
"indexmap", "indexmap",
"lru_time_cache", "lru_time_cache",
"maud", "maud",
@ -2627,6 +2720,15 @@ dependencies = [
"tracing-subscriber", "tracing-subscriber",
] ]
[[package]]
name = "signal-hook-registry"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "siphasher" name = "siphasher"
version = "0.3.11" version = "0.3.11"
@ -2860,7 +2962,9 @@ dependencies = [
"libc", "libc",
"mio", "mio",
"num_cpus", "num_cpus",
"parking_lot",
"pin-project-lite", "pin-project-lite",
"signal-hook-registry",
"socket2", "socket2",
"tokio-macros", "tokio-macros",
"windows-sys 0.48.0", "windows-sys 0.48.0",
@ -3012,6 +3116,7 @@ version = "0.1.40"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
dependencies = [ dependencies = [
"log",
"pin-project-lite", "pin-project-lite",
"tracing-attributes", "tracing-attributes",
"tracing-core", "tracing-core",

View file

@ -14,6 +14,9 @@ edition = "2021"
[dependencies] [dependencies]
badge = { path = "./libs/badge" } badge = { path = "./libs/badge" }
actix-http = { version = "3", features = ["http2"] }
actix-server = "2"
actix-service = "2"
anyhow = "1" anyhow = "1"
cadence = "1" cadence = "1"
crates-index = { version = "2", default-features = false, features = ["git"] } crates-index = { version = "2", default-features = false, features = ["git"] }
@ -21,7 +24,6 @@ derive_more = "0.99"
dotenvy = "0.15" dotenvy = "0.15"
font-awesome-as-a-crate = "0.3" font-awesome-as-a-crate = "0.3"
futures-util = { version = "0.3", default-features = false, features = ["std"] } futures-util = { version = "0.3", default-features = false, features = ["std"] }
hyper = { version = "0.14.10", features = ["full"] }
indexmap = { version = "2", features = ["serde"] } indexmap = { version = "2", features = ["serde"] }
lru_time_cache = "0.11" lru_time_cache = "0.11"
maud = "0.26" maud = "0.26"

View file

@ -1,6 +1,4 @@
use std::env; use std::{env, fs, path::Path};
use std::fs;
use std::path::Path;
use sha1::{Digest, Sha1}; use sha1::{Digest, Sha1};

View file

@ -235,8 +235,8 @@ mod tests {
#[test] #[test]
#[ignore] #[ignore]
fn test_to_svg() { fn test_to_svg() {
use std::fs::File; use std::{fs::File, io::Write as _};
use std::io::Write;
let mut file = File::create("test.svg").unwrap(); let mut file = File::create("test.svg").unwrap();
let options = BadgeOptions { let options = BadgeOptions {
subject: "latest".to_owned(), subject: "latest".to_owned(),

View file

@ -2,11 +2,12 @@ use anyhow::Error;
use futures_util::{future::BoxFuture, stream::FuturesOrdered, FutureExt as _, StreamExt as _}; use futures_util::{future::BoxFuture, stream::FuturesOrdered, FutureExt as _, StreamExt as _};
use relative_path::RelativePathBuf; use relative_path::RelativePathBuf;
use crate::models::repo::RepoPath; use crate::{
engine::{
use crate::engine::{ machines::crawler::{ManifestCrawler, ManifestCrawlerOutput},
machines::crawler::{ManifestCrawler, ManifestCrawlerOutput}, Engine,
Engine, },
models::repo::RepoPath,
}; };
pub async fn crawl_manifest( pub async fn crawl_manifest(

View file

@ -1,5 +1,4 @@
mod analyze; mod analyze;
mod crawl; mod crawl;
pub use self::analyze::analyze_dependencies; pub use self::{analyze::analyze_dependencies, crawl::crawl_manifest};
pub use self::crawl::crawl_manifest;

View file

@ -101,9 +101,8 @@ impl DependencyAnalyzer {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::models::crates::{CrateDep, CrateDeps, CrateRelease};
use super::*; use super::*;
use crate::models::crates::{CrateDep, CrateDeps, CrateRelease};
#[test] #[test]
fn tracks_latest_without_matching() { fn tracks_latest_without_matching() {

View file

@ -4,8 +4,10 @@ use anyhow::Error;
use indexmap::IndexMap; use indexmap::IndexMap;
use relative_path::RelativePathBuf; use relative_path::RelativePathBuf;
use crate::models::crates::{CrateDep, CrateDeps, CrateManifest, CrateName}; use crate::{
use crate::parsers::manifest::parse_manifest_toml; models::crates::{CrateDep, CrateDeps, CrateManifest, CrateName},
parsers::manifest::parse_manifest_toml,
};
pub struct ManifestCrawlerOutput { pub struct ManifestCrawlerOutput {
pub crates: IndexMap<CrateName, CrateDeps>, pub crates: IndexMap<CrateName, CrateDeps>,
@ -118,9 +120,8 @@ mod tests {
use relative_path::RelativePath; use relative_path::RelativePath;
use semver::VersionReq; use semver::VersionReq;
use crate::models::crates::CrateDep;
use super::*; use super::*;
use crate::models::crates::CrateDep;
#[test] #[test]
fn simple_package_manifest() { fn simple_package_manifest() {

View file

@ -5,28 +5,33 @@ use std::{
time::{Duration, Instant}, time::{Duration, Instant},
}; };
use actix_service::Service;
use anyhow::{anyhow, Error}; use anyhow::{anyhow, Error};
use cadence::{MetricSink, NopMetricSink, StatsdClient}; use cadence::{MetricSink, NopMetricSink, StatsdClient};
use futures_util::{ use futures_util::{
future::try_join_all, future::try_join_all,
stream::{self, BoxStream}, stream::{self, BoxStream},
StreamExt as _, StreamExt as _,
}; };
use hyper::service::Service;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use relative_path::{RelativePath, RelativePathBuf}; use relative_path::{RelativePath, RelativePathBuf};
use rustsec::database::Database; use rustsec::database::Database;
use semver::VersionReq; use semver::VersionReq;
use crate::interactors::crates::{GetPopularCrates, QueryCrate}; use crate::{
use crate::interactors::github::GetPopularRepos; interactors::{
use crate::interactors::rustsec::FetchAdvisoryDatabase; crates::{GetPopularCrates, QueryCrate},
use crate::interactors::RetrieveFileAtPath; github::GetPopularRepos,
use crate::models::crates::{AnalyzedDependencies, CrateName, CratePath, CrateRelease}; rustsec::FetchAdvisoryDatabase,
use crate::models::repo::{RepoPath, Repository}; RetrieveFileAtPath,
use crate::utils::cache::Cache; },
use crate::ManagedIndex; models::{
crates::{AnalyzedDependencies, CrateName, CratePath, CrateRelease},
repo::{RepoPath, Repository},
},
utils::cache::Cache,
ManagedIndex,
};
mod fut; mod fut;
mod machines; mod machines;
@ -272,7 +277,7 @@ impl Engine {
) -> Result<String, Error> { ) -> Result<String, Error> {
let manifest_path = path.join(RelativePath::new("Cargo.toml")); let manifest_path = path.join(RelativePath::new("Cargo.toml"));
let mut service = self.retrieve_file_at_path.clone(); let service = self.retrieve_file_at_path.clone();
service.call((repo_path.clone(), manifest_path)).await service.call((repo_path.clone(), manifest_path)).await
} }

View file

@ -1,12 +1,11 @@
use std::{fmt, str, task::Context, task::Poll}; use std::fmt;
use actix_service::Service;
use anyhow::{anyhow, Error}; use anyhow::{anyhow, Error};
use crates_index::{Crate, DependencyKind}; use crates_index::{Crate, DependencyKind};
use futures_util::FutureExt as _; use futures_util::FutureExt as _;
use hyper::service::Service;
use semver::{Version, VersionReq}; use semver::{Version, VersionReq};
use serde::Deserialize; use serde::Deserialize;
use tokio::task::spawn_blocking; use tokio::task::spawn_blocking;
use crate::{ use crate::{
@ -86,11 +85,9 @@ impl Service<CrateName> for QueryCrate {
type Error = Error; type Error = Error;
type Future = BoxFuture<Result<Self::Response, Self::Error>>; type Future = BoxFuture<Result<Self::Response, Self::Error>>;
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { actix_service::always_ready!();
Poll::Ready(Ok(()))
}
fn call(&mut self, crate_name: CrateName) -> Self::Future { fn call(&self, crate_name: CrateName) -> Self::Future {
let index = self.index.clone(); let index = self.index.clone();
Self::query(index, crate_name).boxed() Self::query(index, crate_name).boxed()
} }
@ -150,11 +147,9 @@ impl Service<()> for GetPopularCrates {
type Error = Error; type Error = Error;
type Future = BoxFuture<Result<Self::Response, Self::Error>>; type Future = BoxFuture<Result<Self::Response, Self::Error>>;
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { actix_service::always_ready!();
Poll::Ready(Ok(()))
}
fn call(&mut self, _req: ()) -> Self::Future { fn call(&self, _req: ()) -> Self::Future {
let client = self.client.clone(); let client = self.client.clone();
Self::query(client).boxed() Self::query(client).boxed()
} }

View file

@ -1,12 +1,8 @@
use std::{ use std::fmt;
fmt,
task::{Context, Poll},
};
use actix_service::Service;
use anyhow::Error; use anyhow::Error;
use futures_util::FutureExt as _; use futures_util::FutureExt as _;
use hyper::service::Service;
use serde::Deserialize; use serde::Deserialize;
use crate::{ use crate::{
@ -75,11 +71,9 @@ impl Service<()> for GetPopularRepos {
type Error = Error; type Error = Error;
type Future = BoxFuture<Result<Self::Response, Self::Error>>; type Future = BoxFuture<Result<Self::Response, Self::Error>>;
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { actix_service::always_ready!();
Poll::Ready(Ok(()))
}
fn call(&mut self, _req: ()) -> Self::Future { fn call(&self, _req: ()) -> Self::Future {
let client = self.client.clone(); let client = self.client.clone();
Self::query(client).boxed() Self::query(client).boxed()
} }

View file

@ -1,11 +1,8 @@
use std::{ use std::fmt;
fmt,
task::{Context, Poll},
};
use actix_service::Service;
use anyhow::{anyhow, Error}; use anyhow::{anyhow, Error};
use futures_util::FutureExt as _; use futures_util::FutureExt as _;
use hyper::service::Service;
use relative_path::RelativePathBuf; use relative_path::RelativePathBuf;
use crate::{models::repo::RepoPath, BoxFuture}; use crate::{models::repo::RepoPath, BoxFuture};
@ -45,11 +42,9 @@ impl Service<(RepoPath, RelativePathBuf)> for RetrieveFileAtPath {
type Error = Error; type Error = Error;
type Future = BoxFuture<Result<Self::Response, Self::Error>>; type Future = BoxFuture<Result<Self::Response, Self::Error>>;
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { actix_service::always_ready!();
Poll::Ready(Ok(()))
}
fn call(&mut self, (repo_path, path): (RepoPath, RelativePathBuf)) -> Self::Future { fn call(&self, (repo_path, path): (RepoPath, RelativePathBuf)) -> Self::Future {
let client = self.client.clone(); let client = self.client.clone();
Self::query(client, repo_path, path).boxed() Self::query(client, repo_path, path).boxed()
} }
@ -57,6 +52,6 @@ impl Service<(RepoPath, RelativePathBuf)> for RetrieveFileAtPath {
impl fmt::Debug for RetrieveFileAtPath { impl fmt::Debug for RetrieveFileAtPath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("RetrieveFileAtPath") f.debug_struct("RetrieveFileAtPath").finish_non_exhaustive()
} }
} }

View file

@ -1,8 +1,8 @@
use std::{fmt, sync::Arc, task::Context, task::Poll}; use std::{fmt, sync::Arc};
use actix_service::Service;
use anyhow::Error; use anyhow::Error;
use futures_util::FutureExt as _; use futures_util::FutureExt as _;
use hyper::service::Service;
use rustsec::database::Database; use rustsec::database::Database;
use crate::BoxFuture; use crate::BoxFuture;
@ -28,11 +28,9 @@ impl Service<()> for FetchAdvisoryDatabase {
type Error = Error; type Error = Error;
type Future = BoxFuture<Result<Self::Response, Self::Error>>; type Future = BoxFuture<Result<Self::Response, Self::Error>>;
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { actix_service::always_ready!();
Poll::Ready(Ok(()))
}
fn call(&mut self, _req: ()) -> Self::Future { fn call(&self, _req: ()) -> Self::Future {
let client = self.client.clone(); let client = self.client.clone();
Self::fetch(client).boxed() Self::fetch(client).boxed()
} }

View file

@ -4,18 +4,14 @@
use std::{ use std::{
env, env,
future::Future, future::Future,
net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket}, net::{Ipv4Addr, UdpSocket},
pin::Pin, pin::Pin,
time::Duration, time::Duration,
}; };
use actix_http::{HttpService, Request};
use actix_server::Server;
use cadence::{QueuingMetricSink, UdpMetricSink}; use cadence::{QueuingMetricSink, UdpMetricSink};
use hyper::{
server::conn::AddrStream,
service::{make_service_fn, service_fn},
Server,
};
use reqwest::redirect::Policy as RedirectPolicy; use reqwest::redirect::Policy as RedirectPolicy;
use tracing::Instrument as _; use tracing::Instrument as _;
@ -26,9 +22,7 @@ mod parsers;
mod server; mod server;
mod utils; mod utils;
use self::engine::Engine; use self::{engine::Engine, server::App, utils::index::ManagedIndex};
use self::server::App;
use self::utils::index::ManagedIndex;
/// Future crate's BoxFuture without the explicit lifetime parameter. /// Future crate's BoxFuture without the explicit lifetime parameter.
pub type BoxFuture<T> = Pin<Box<dyn Future<Output = T> + Send>>; pub type BoxFuture<T> = Pin<Box<dyn Future<Output = T> + Send>>;
@ -80,8 +74,6 @@ async fn main() {
.parse() .parse()
.expect("could not read port"); .expect("could not read port");
let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), port);
let index = ManagedIndex::new(); let index = ManagedIndex::new();
{ {
@ -95,25 +87,30 @@ async fn main() {
let mut engine = Engine::new(client.clone(), index); let mut engine = Engine::new(client.clone(), index);
engine.set_metrics(metrics); engine.set_metrics(metrics);
let make_svc = make_service_fn(move |_socket: &AddrStream| { let server = Server::build()
let engine = engine.clone(); .bind("deps-rs", (Ipv4Addr::UNSPECIFIED, port), move || {
let engine = engine.clone();
async move { let app = App::new(engine.clone());
let server = App::new(engine.clone());
Ok::<_, hyper::Error>(service_fn(move |req| {
let server = server.clone();
async move {
let path = req.uri().path().to_owned();
server HttpService::build()
.handle(req) .client_disconnect_timeout(Duration::from_secs(5))
.instrument(tracing::info_span!("@", %path)) .client_request_timeout(Duration::from_secs(5))
.await .finish(move |req: Request| {
} let app = app.clone();
}))
} async move {
}); let path = req.path().to_owned();
let server = Server::bind(&addr).serve(make_svc);
app.handle(req)
.instrument(tracing::info_span!("@", %path))
.await
}
})
.tcp_auto_h2c()
})
.unwrap()
.run();
tracing::info!("Server running on port {port}"); tracing::info!("Server running on port {port}");

View file

@ -135,9 +135,8 @@ pub fn parse_manifest_toml(input: &str) -> Result<CrateManifest, Error> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::models::crates::CrateManifest;
use super::*; use super::*;
use crate::models::crates::CrateManifest;
#[test] #[test]
fn parse_workspace_without_members_declaration() { fn parse_workspace_without_members_declaration() {

View file

@ -1,11 +1,12 @@
use std::{env, sync::Arc, time::Instant}; use std::{env, sync::Arc, time::Instant};
use actix_http::{
body::MessageBody,
header::{CACHE_CONTROL, CONTENT_TYPE, ETAG, LOCATION},
Method, Request, Response, StatusCode,
};
use badge::BadgeStyle; use badge::BadgeStyle;
use futures_util::future; use futures_util::future;
use hyper::{
header::{CACHE_CONTROL, CONTENT_TYPE, ETAG, LOCATION},
Body, Error as HyperError, Method, Request, Response, StatusCode,
};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use route_recognizer::{Params, Router}; use route_recognizer::{Params, Router};
use semver::VersionReq; use semver::VersionReq;
@ -17,10 +18,14 @@ mod views;
use self::assets::{ use self::assets::{
STATIC_LINKS_JS_ETAG, STATIC_LINKS_JS_PATH, STATIC_STYLE_CSS_ETAG, STATIC_STYLE_CSS_PATH, STATIC_LINKS_JS_ETAG, STATIC_LINKS_JS_PATH, STATIC_STYLE_CSS_ETAG, STATIC_STYLE_CSS_PATH,
}; };
use crate::engine::{AnalyzeDependenciesOutcome, Engine}; use crate::{
use crate::models::crates::{CrateName, CratePath}; engine::{AnalyzeDependenciesOutcome, Engine},
use crate::models::repo::RepoPath; models::{
use crate::models::SubjectPath; crates::{CrateName, CratePath},
repo::RepoPath,
SubjectPath,
},
};
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
enum StatusFormat { enum StatusFormat {
@ -86,7 +91,10 @@ impl App {
} }
} }
pub async fn handle(&self, req: Request<Body>) -> Result<Response<Body>, HyperError> { pub async fn handle(
&self,
req: Request,
) -> Result<Response<impl MessageBody>, actix_http::Error> {
let start = Instant::now(); let start = Instant::now();
// allows `/path/` to also match `/path` // allows `/path/` to also match `/path`
@ -94,33 +102,39 @@ impl App {
let res = if let Ok(route_match) = self.router.recognize(normalized_path) { let res = if let Ok(route_match) = self.router.recognize(normalized_path) {
match (req.method(), route_match.handler()) { match (req.method(), route_match.handler()) {
(&Method::GET, Route::Index) => self.index(req, route_match.params().clone()).await, (&Method::GET, Route::Index) => self
.index(req, route_match.params().clone())
.await
.map(Response::map_into_boxed_body),
(&Method::GET, Route::RepoStatus(format)) => { (&Method::GET, Route::RepoStatus(format)) => self
self.repo_status(req, route_match.params().clone(), *format) .repo_status(req, route_match.params().clone(), *format)
.await .await
.map(Response::map_into_boxed_body),
(&Method::GET, Route::CrateStatus(format)) => self
.crate_status(req, route_match.params().clone(), *format)
.await
.map(Response::map_into_boxed_body),
(&Method::GET, Route::LatestCrateBadge) => self
.crate_status(req, route_match.params().clone(), StatusFormat::Svg)
.await
.map(Response::map_into_boxed_body),
(&Method::GET, Route::CrateRedirect) => self
.crate_redirect(req, route_match.params().clone())
.await
.map(Response::map_into_boxed_body),
(&Method::GET, Route::Static(file)) => {
Ok(App::static_file(*file).map_into_boxed_body())
} }
(&Method::GET, Route::CrateStatus(format)) => { _ => Ok(not_found().map_into_boxed_body()),
self.crate_status(req, route_match.params().clone(), *format)
.await
}
(&Method::GET, Route::LatestCrateBadge) => {
self.crate_status(req, route_match.params().clone(), StatusFormat::Svg)
.await
}
(&Method::GET, Route::CrateRedirect) => {
self.crate_redirect(req, route_match.params().clone()).await
}
(&Method::GET, Route::Static(file)) => Ok(App::static_file(*file)),
_ => Ok(not_found()),
} }
} else { } else {
Ok(not_found()) Ok(not_found().map_into_boxed_body())
}; };
let end = Instant::now(); let end = Instant::now();
@ -141,9 +155,9 @@ impl App {
impl App { impl App {
async fn index( async fn index(
&self, &self,
_req: Request<Body>, _req: Request,
_params: Params, _params: Params,
) -> Result<Response<Body>, HyperError> { ) -> Result<Response<impl MessageBody>, actix_http::Error> {
let engine = self.engine.clone(); let engine = self.engine.clone();
let popular = let popular =
@ -155,20 +169,21 @@ impl App {
let mut response = let mut response =
views::html::error::render("Could not retrieve popular items", ""); views::html::error::render("Could not retrieve popular items", "");
*response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
Ok(response)
Ok(response.map_into_boxed_body())
} }
Ok((popular_repos, popular_crates)) => { Ok((popular_repos, popular_crates)) => {
Ok(views::html::index::render(popular_repos, popular_crates)) Ok(views::html::index::render(popular_repos, popular_crates).map_into_boxed_body())
} }
} }
} }
async fn repo_status( async fn repo_status(
&self, &self,
req: Request<Body>, req: Request,
params: Params, params: Params,
format: StatusFormat, format: StatusFormat,
) -> Result<Response<Body>, HyperError> { ) -> Result<Response<impl MessageBody>, actix_http::Error> {
let server = self.clone(); let server = self.clone();
let site = params.find("site").expect("route param 'site' not found"); let site = params.find("site").expect("route param 'site' not found");
@ -187,7 +202,8 @@ impl App {
"Please make sure to provide a valid repository path.", "Please make sure to provide a valid repository path.",
); );
*response.status_mut() = StatusCode::BAD_REQUEST; *response.status_mut() = StatusCode::BAD_REQUEST;
Ok(response)
Ok(response.map_into_boxed_body())
} }
Ok(repo_path) => { Ok(repo_path) => {
@ -205,7 +221,8 @@ impl App {
SubjectPath::Repo(repo_path), SubjectPath::Repo(repo_path),
extra_knobs, extra_knobs,
); );
Ok(response)
Ok(response.map_into_boxed_body())
} }
Ok(analysis_outcome) => { Ok(analysis_outcome) => {
let response = App::status_format_analysis( let response = App::status_format_analysis(
@ -214,7 +231,8 @@ impl App {
SubjectPath::Repo(repo_path), SubjectPath::Repo(repo_path),
extra_knobs, extra_knobs,
); );
Ok(response)
Ok(response.map_into_boxed_body())
} }
} }
} }
@ -223,9 +241,9 @@ impl App {
async fn crate_redirect( async fn crate_redirect(
&self, &self,
_req: Request<Body>, _req: Request,
params: Params, params: Params,
) -> Result<Response<Body>, HyperError> { ) -> Result<Response<impl MessageBody>, actix_http::Error> {
let engine = self.engine.clone(); let engine = self.engine.clone();
let name = params.find("name").expect("route param 'name' not found"); let name = params.find("name").expect("route param 'name' not found");
@ -239,7 +257,8 @@ impl App {
"Please make sure to provide a valid crate name.", "Please make sure to provide a valid crate name.",
); );
*response.status_mut() = StatusCode::BAD_REQUEST; *response.status_mut() = StatusCode::BAD_REQUEST;
Ok(response)
Ok(response.map_into_boxed_body())
} }
Ok(crate_name) => { Ok(crate_name) => {
@ -255,7 +274,8 @@ impl App {
"Please make sure to provide a valid crate name.", "Please make sure to provide a valid crate name.",
); );
*response.status_mut() = StatusCode::NOT_FOUND; *response.status_mut() = StatusCode::NOT_FOUND;
Ok(response)
Ok(response.map_into_boxed_body())
} }
Ok(None) => { Ok(None) => {
let mut response = views::html::error::render( let mut response = views::html::error::render(
@ -263,7 +283,8 @@ impl App {
"Please make sure to provide a valid crate name.", "Please make sure to provide a valid crate name.",
); );
*response.status_mut() = StatusCode::NOT_FOUND; *response.status_mut() = StatusCode::NOT_FOUND;
Ok(response)
Ok(response.map_into_boxed_body())
} }
Ok(Some(release)) => { Ok(Some(release)) => {
let redirect_url = format!( let redirect_url = format!(
@ -273,13 +294,11 @@ impl App {
release.version release.version
); );
let res = Response::builder() let res = Response::build(StatusCode::TEMPORARY_REDIRECT)
.status(StatusCode::TEMPORARY_REDIRECT) .insert_header((LOCATION, redirect_url))
.header(LOCATION, redirect_url) .finish();
.body(Body::empty())
.unwrap();
Ok(res) Ok(res.map_into_boxed_body())
} }
} }
} }
@ -288,10 +307,10 @@ impl App {
async fn crate_status( async fn crate_status(
&self, &self,
req: Request<Body>, req: Request,
params: Params, params: Params,
format: StatusFormat, format: StatusFormat,
) -> Result<Response<Body>, HyperError> { ) -> Result<Response<impl MessageBody>, actix_http::Error> {
let server = self.clone(); let server = self.clone();
let name = params.find("name").expect("route param 'name' not found"); let name = params.find("name").expect("route param 'name' not found");
@ -307,7 +326,8 @@ impl App {
"Please make sure to provide a valid crate name and version.", "Please make sure to provide a valid crate name and version.",
); );
*response.status_mut() = StatusCode::BAD_REQUEST; *response.status_mut() = StatusCode::BAD_REQUEST;
return Ok(response);
return Ok(response.map_into_boxed_body());
} }
}; };
@ -317,7 +337,7 @@ impl App {
.await .await
{ {
Ok(Some(latest_rel)) => latest_rel.version.to_string(), Ok(Some(latest_rel)) => latest_rel.version.to_string(),
Ok(None) => return Ok(not_found()), Ok(None) => return Ok(not_found().map_into_boxed_body()),
Err(err) => { Err(err) => {
tracing::error!(%err); tracing::error!(%err);
let mut response = views::html::error::render( let mut response = views::html::error::render(
@ -325,7 +345,8 @@ impl App {
"Please make sure to provide a valid crate name.", "Please make sure to provide a valid crate name.",
); );
*response.status_mut() = StatusCode::NOT_FOUND; *response.status_mut() = StatusCode::NOT_FOUND;
return Ok(response);
return Ok(response.map_into_boxed_body());
} }
} }
} }
@ -342,8 +363,10 @@ impl App {
"Please make sure to provide a valid crate name and version.", "Please make sure to provide a valid crate name and version.",
); );
*response.status_mut() = StatusCode::BAD_REQUEST; *response.status_mut() = StatusCode::BAD_REQUEST;
Ok(response)
Ok(response.map_into_boxed_body())
} }
Ok(crate_path) => { Ok(crate_path) => {
let analyze_result = server let analyze_result = server
.engine .engine
@ -359,7 +382,8 @@ impl App {
SubjectPath::Crate(crate_path), SubjectPath::Crate(crate_path),
badge_knobs, badge_knobs,
); );
Ok(response)
Ok(response.map_into_boxed_body())
} }
Ok(analysis_outcome) => { Ok(analysis_outcome) => {
let response = App::status_format_analysis( let response = App::status_format_analysis(
@ -369,7 +393,7 @@ impl App {
badge_knobs, badge_knobs,
); );
Ok(response) Ok(response.map_into_boxed_body())
} }
} }
} }
@ -381,38 +405,41 @@ impl App {
format: StatusFormat, format: StatusFormat,
subject_path: SubjectPath, subject_path: SubjectPath,
badge_knobs: ExtraConfig, badge_knobs: ExtraConfig,
) -> Response<Body> { ) -> Response<impl MessageBody> {
match format { match format {
StatusFormat::Svg => views::badge::response(analysis_outcome.as_ref(), badge_knobs), StatusFormat::Svg => {
views::badge::response(analysis_outcome.as_ref(), badge_knobs).map_into_boxed_body()
}
StatusFormat::Html => { StatusFormat::Html => {
views::html::status::render(analysis_outcome, subject_path, badge_knobs) views::html::status::render(analysis_outcome, subject_path, badge_knobs)
.map_into_boxed_body()
} }
} }
} }
fn static_file(file: StaticFile) -> Response<Body> { fn static_file(file: StaticFile) -> Response<impl MessageBody> {
match file { match file {
StaticFile::StyleCss => Response::builder() StaticFile::StyleCss => Response::build(StatusCode::OK)
.header(CONTENT_TYPE, "text/css; charset=utf-8") .insert_header((CONTENT_TYPE, "text/css; charset=utf-8"))
.header(ETAG, STATIC_STYLE_CSS_ETAG) .insert_header((ETAG, STATIC_STYLE_CSS_ETAG))
.header(CACHE_CONTROL, "public, max-age=365000000, immutable") .insert_header((CACHE_CONTROL, "public, max-age=365000000, immutable"))
.body(Body::from(assets::STATIC_STYLE_CSS)) .body(assets::STATIC_STYLE_CSS),
.unwrap(),
StaticFile::FaviconPng => Response::builder() StaticFile::FaviconPng => Response::build(StatusCode::OK)
.header(CONTENT_TYPE, "image/svg+xml") .insert_header((CONTENT_TYPE, "image/svg+xml"))
.body(Body::from(assets::STATIC_FAVICON)) .body(assets::STATIC_FAVICON),
.unwrap(),
StaticFile::LinksJs => Response::builder() StaticFile::LinksJs => Response::build(StatusCode::OK)
.header(CONTENT_TYPE, "text/javascript; charset=utf-8") .insert_header((CONTENT_TYPE, "text/javascript; charset=utf-8"))
.header(ETAG, STATIC_LINKS_JS_ETAG) .insert_header((ETAG, STATIC_LINKS_JS_ETAG))
.header(CACHE_CONTROL, "public, max-age=365000000, immutable") .insert_header((CACHE_CONTROL, "public, max-age=365000000, immutable"))
.body(Body::from(assets::STATIC_LINKS_JS)) .body(assets::STATIC_LINKS_JS),
.unwrap(),
} }
} }
} }
fn not_found() -> Response<Body> { fn not_found() -> Response<impl MessageBody> {
views::html::error::render_404() views::html::error::render_404()
} }

View file

@ -1,9 +1,7 @@
use actix_http::{body::MessageBody, header::CONTENT_TYPE, Response, StatusCode};
use badge::{Badge, BadgeOptions}; use badge::{Badge, BadgeOptions};
use hyper::header::CONTENT_TYPE;
use hyper::{Body, Response};
use crate::engine::AnalyzeDependenciesOutcome; use crate::{engine::AnalyzeDependenciesOutcome, server::ExtraConfig};
use crate::server::ExtraConfig;
pub fn badge( pub fn badge(
analysis_outcome: Option<&AnalyzeDependenciesOutcome>, analysis_outcome: Option<&AnalyzeDependenciesOutcome>,
@ -75,11 +73,10 @@ pub fn badge(
pub fn response( pub fn response(
analysis_outcome: Option<&AnalyzeDependenciesOutcome>, analysis_outcome: Option<&AnalyzeDependenciesOutcome>,
badge_knobs: ExtraConfig, badge_knobs: ExtraConfig,
) -> Response<Body> { ) -> Response<impl MessageBody> {
let badge = badge(analysis_outcome, badge_knobs).to_svg(); let badge = badge(analysis_outcome, badge_knobs).to_svg();
Response::builder() Response::build(StatusCode::OK)
.header(CONTENT_TYPE, "image/svg+xml; charset=utf-8") .insert_header((CONTENT_TYPE, "image/svg+xml; charset=utf-8"))
.body(Body::from(badge)) .body(badge)
.unwrap()
} }

View file

@ -1,12 +1,13 @@
use hyper::{ use actix_http::{
body::MessageBody,
header::{CACHE_CONTROL, CONTENT_TYPE}, header::{CACHE_CONTROL, CONTENT_TYPE},
Body, Response, StatusCode, Response, StatusCode,
}; };
use maud::html; use maud::html;
use crate::server::assets::STATIC_STYLE_CSS_PATH; use crate::server::assets::STATIC_STYLE_CSS_PATH;
pub fn render(title: &str, descr: &str) -> Response<Body> { pub fn render(title: &str, descr: &str) -> Response<impl MessageBody> {
super::render_html( super::render_html(
title, title,
html! { html! {
@ -26,7 +27,7 @@ pub fn render(title: &str, descr: &str) -> Response<Body> {
) )
} }
pub fn render_404() -> Response<Body> { pub fn render_404() -> Response<impl MessageBody> {
let rendered = html! { let rendered = html! {
html { html {
head { head {
@ -55,10 +56,8 @@ pub fn render_404() -> Response<Body> {
} }
}; };
Response::builder() Response::build(StatusCode::NOT_FOUND)
.status(StatusCode::NOT_FOUND) .insert_header((CONTENT_TYPE, "text/html; charset=utf-8"))
.header(CONTENT_TYPE, "text/html; charset=utf-8") .insert_header((CACHE_CONTROL, "public, max-age=300, immutable"))
.header(CACHE_CONTROL, "public, max-age=300, immutable") .body(rendered.0)
.body(Body::from(rendered.0))
.unwrap()
} }

View file

@ -1,10 +1,10 @@
use hyper::{Body, Response}; use actix_http::{body::MessageBody, Response};
use maud::{html, Markup}; use maud::{html, Markup};
use crate::models::crates::CratePath; use crate::{
use crate::models::repo::Repository; models::{crates::CratePath, repo::Repository},
server::assets::STATIC_LINKS_JS_PATH,
use crate::server::assets::STATIC_LINKS_JS_PATH; };
fn link_forms() -> Markup { fn link_forms() -> Markup {
html! { html! {
@ -161,7 +161,10 @@ fn popular_table(popular_repos: Vec<Repository>, popular_crates: Vec<CratePath>)
} }
} }
pub fn render(popular_repos: Vec<Repository>, popular_crates: Vec<CratePath>) -> Response<Body> { pub fn render(
popular_repos: Vec<Repository>,
popular_crates: Vec<CratePath>,
) -> Response<impl MessageBody> {
super::render_html( super::render_html(
"Keep your dependencies up-to-date", "Keep your dependencies up-to-date",
html! { html! {

View file

@ -1,17 +1,15 @@
use std::time::Duration; use std::time::Duration;
use hyper::header::CONTENT_TYPE; use actix_http::{body::MessageBody, header::CONTENT_TYPE, Response, StatusCode};
use hyper::{Body, Response};
use maud::{html, Markup, Render, DOCTYPE}; use maud::{html, Markup, Render, DOCTYPE};
pub mod error; pub mod error;
pub mod index; pub mod index;
pub mod status; pub mod status;
use crate::server::assets::STATIC_STYLE_CSS_PATH; use crate::server::{assets::STATIC_STYLE_CSS_PATH, SELF_BASE_URL};
use crate::server::SELF_BASE_URL;
fn render_html<B: Render>(title: &str, body: B) -> Response<Body> { fn render_html<B: Render>(title: &str, body: B) -> Response<impl MessageBody> {
let rendered = html! { let rendered = html! {
(DOCTYPE) (DOCTYPE)
html { html {
@ -28,10 +26,9 @@ fn render_html<B: Render>(title: &str, body: B) -> Response<Body> {
} }
}; };
Response::builder() Response::build(StatusCode::OK)
.header(CONTENT_TYPE, "text/html; charset=utf-8") .insert_header((CONTENT_TYPE, "text/html; charset=utf-8"))
.body(Body::from(rendered.0)) .body(rendered.0)
.unwrap()
} }
fn render_navbar() -> Markup { fn render_navbar() -> Markup {

View file

@ -1,17 +1,20 @@
use actix_http::{body::MessageBody, Response};
use font_awesome_as_a_crate::{svg as fa, Type as FaType}; use font_awesome_as_a_crate::{svg as fa, Type as FaType};
use hyper::{Body, Response};
use indexmap::IndexMap; use indexmap::IndexMap;
use maud::{html, Markup, PreEscaped}; use maud::{html, Markup, PreEscaped};
use pulldown_cmark::{html, Parser}; use pulldown_cmark::{html, Parser};
use rustsec::advisory::Advisory; use rustsec::advisory::Advisory;
use semver::Version; use semver::Version;
use crate::engine::AnalyzeDependenciesOutcome; use crate::{
use crate::models::crates::{AnalyzedDependencies, AnalyzedDependency, CrateName}; engine::AnalyzeDependenciesOutcome,
use crate::models::repo::RepoSite; models::{
use crate::models::SubjectPath; crates::{AnalyzedDependencies, AnalyzedDependency, CrateName},
use crate::server::views::badge; repo::RepoSite,
use crate::server::ExtraConfig; SubjectPath,
},
server::{views::badge, ExtraConfig},
};
fn get_crates_url(name: impl AsRef<str>) -> String { fn get_crates_url(name: impl AsRef<str>) -> String {
format!("https://crates.io/crates/{}", name.as_ref()) format!("https://crates.io/crates/{}", name.as_ref())
@ -454,7 +457,7 @@ pub fn render(
analysis_outcome: Option<AnalyzeDependenciesOutcome>, analysis_outcome: Option<AnalyzeDependenciesOutcome>,
subject_path: SubjectPath, subject_path: SubjectPath,
extra_config: ExtraConfig, extra_config: ExtraConfig,
) -> Response<Body> { ) -> Response<impl MessageBody> {
let title = match subject_path { let title = match subject_path {
SubjectPath::Repo(ref repo_path) => { SubjectPath::Repo(ref repo_path) => {
format!("{} / {}", repo_path.qual.as_ref(), repo_path.name.as_ref()) format!("{} / {}", repo_path.qual.as_ref(), repo_path.name.as_ref())

View file

@ -1,7 +1,7 @@
use std::{fmt, sync::Arc, time::Duration}; use std::{fmt, sync::Arc, time::Duration};
use actix_service::Service;
use derive_more::{Display, Error, From}; use derive_more::{Display, Error, From};
use hyper::service::Service;
use lru_time_cache::LruCache; use lru_time_cache::LruCache;
use tokio::sync::Mutex; use tokio::sync::Mutex;
@ -26,7 +26,7 @@ where
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct("Cache") fmt.debug_struct("Cache")
.field("inner", &self.inner) .field("inner", &self.inner)
.finish() .finish_non_exhaustive()
} }
} }
@ -65,7 +65,7 @@ where
cache = "miss", cache = "miss",
); );
let mut service = self.inner.clone(); let service = self.inner.clone();
let fresh = service.call(req.clone()).await?; let fresh = service.call(req.clone()).await?;
{ {

View file

@ -1,13 +1,16 @@
use std::sync::Arc; use std::{
use std::sync::Mutex; sync::{Arc, Mutex},
use std::time::Duration; time::Duration,
};
use anyhow::Result;
use crates_index::{Crate, GitIndex};
use tokio::{
task::spawn_blocking,
time::{self, MissedTickBehavior},
};
use crate::models::crates::CrateName; use crate::models::crates::CrateName;
use anyhow::Result;
use crates_index::Crate;
use crates_index::GitIndex;
use tokio::task::spawn_blocking;
use tokio::time::{self, MissedTickBehavior};
#[derive(Clone)] #[derive(Clone)]
pub struct ManagedIndex { pub struct ManagedIndex {