From 85a077e80daad53b0b5475d626fc91f46f950216 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sun, 26 May 2024 21:36:03 +0100 Subject: [PATCH 1/3] refactor: migrate to tracing for logging --- .gitignore | 3 + .rustfmt.toml | 3 + Cargo.lock | 242 ++++++++++++++++++-------------- Cargo.toml | 8 +- build.rs | 4 +- libs/badge/badge.rs | 4 +- src/engine/fut/crawl.rs | 11 +- src/engine/fut/mod.rs | 3 +- src/engine/machines/analyzer.rs | 3 +- src/engine/machines/crawler.rs | 9 +- src/engine/mod.rs | 36 +++-- src/interactors/crates.rs | 6 +- src/interactors/github.rs | 1 - src/interactors/rustsec.rs | 6 +- src/main.rs | 57 +++++--- src/parsers/manifest.rs | 3 +- src/server/mod.rs | 61 ++++---- src/server/views/badge.rs | 6 +- src/server/views/html/index.rs | 8 +- src/server/views/html/mod.rs | 6 +- src/server/views/html/status.rs | 15 +- src/utils/cache.rs | 21 ++- src/utils/index.rs | 46 +++--- 23 files changed, 300 insertions(+), 262 deletions(-) create mode 100644 .rustfmt.toml diff --git a/.gitignore b/.gitignore index f2e972d..a31a4f3 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,6 @@ # These are backup files generated by rustfmt **/*.rs.bk + +# local environment variables +.env diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 0000000..efd3c5b --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1,3 @@ +imports_granularity = "Crate" +group_imports = "StdExternalCrate" +use_field_init_shorthand = true diff --git a/Cargo.lock b/Cargo.lock index 9721623..66a9d16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -35,6 +35,15 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "allocator-api2" version = "0.2.18" @@ -144,7 +153,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" dependencies = [ "memchr", - "regex-automata", + "regex-automata 0.4.6", "serde", ] @@ -380,25 +389,10 @@ dependencies = [ ] [[package]] -name = "dirs-next" -version = "2.0.0" +name = "dotenvy" +version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" -dependencies = [ - "cfg-if", - "dirs-sys-next", -] - -[[package]] -name = "dirs-sys-next" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users", - "winapi", -] +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" [[package]] name = "dunce" @@ -437,6 +431,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "error_reporter" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31ae425815400e5ed474178a7a22e275a9687086a12ca63ec793ff292d8fdae8" + [[package]] name = "faster-hex" version = "0.9.0" @@ -1673,17 +1673,6 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" -[[package]] -name = "is-terminal" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" -dependencies = [ - "hermit-abi", - "libc", - "windows-sys 0.52.0", -] - [[package]] name = "itoa" version = "1.0.11" @@ -1739,16 +1728,6 @@ version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" -[[package]] -name = "libredox" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" -dependencies = [ - "bitflags 2.5.0", - "libc", -] - [[package]] name = "linux-raw-sys" version = "0.4.14" @@ -1777,6 +1756,15 @@ version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9106e1d747ffd48e6be5bb2d97fa706ed25b144fbee4d5c02eae110cd8d6badd" +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "maud" version = "0.26.0" @@ -1869,6 +1857,16 @@ dependencies = [ "tempfile", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -1953,6 +1951,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "owned_ttf_parser" version = "0.15.2" @@ -2206,14 +2210,24 @@ dependencies = [ ] [[package]] -name = "redox_users" -version = "0.4.5" +name = "regex" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ - "getrandom", - "libredox", - "thiserror", + "aho-corasick", + "memchr", + "regex-automata 0.4.6", + "regex-syntax 0.8.3", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", ] [[package]] @@ -2221,6 +2235,23 @@ name = "regex-automata" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.3", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "relative-path" @@ -2422,12 +2453,6 @@ dependencies = [ "owned_ttf_parser", ] -[[package]] -name = "rustversion" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" - [[package]] name = "ryu" version = "1.0.18" @@ -2559,6 +2584,15 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shell-words" version = "1.1.0" @@ -2574,6 +2608,8 @@ dependencies = [ "cadence", "crates-index", "derive_more", + "dotenvy", + "error_reporter", "font-awesome-as-a-crate", "futures-util", "gix", @@ -2583,6 +2619,7 @@ dependencies = [ "lru_time_cache", "maud", "once_cell", + "parking_lot", "pulldown-cmark", "relative-path", "reqwest", @@ -2592,11 +2629,10 @@ dependencies = [ "serde", "serde_urlencoded", "sha-1", - "slog", - "slog-async", - "slog-term", "tokio", "toml 0.8.13", + "tracing", + "tracing-subscriber", ] [[package]] @@ -2614,37 +2650,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "slog" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8347046d4ebd943127157b94d63abb990fcf729dc4e9978927fdf4ac3c998d06" - -[[package]] -name = "slog-async" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72c8038f898a2c79507940990f05386455b3a317d8f18d4caea7cbc3d5096b84" -dependencies = [ - "crossbeam-channel", - "slog", - "take_mut", - "thread_local", -] - -[[package]] -name = "slog-term" -version = "2.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6e022d0b998abfe5c3782c1f03551a596269450ccd677ea51c56f8b214610e8" -dependencies = [ - "is-terminal", - "slog", - "term", - "thread_local", - "time", -] - [[package]] name = "smallvec" version = "1.13.2" @@ -2737,12 +2742,6 @@ dependencies = [ "libc", ] -[[package]] -name = "take_mut" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" - [[package]] name = "tame-index" version = "0.12.0" @@ -2780,17 +2779,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "term" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" -dependencies = [ - "dirs-next", - "rustversion", - "winapi", -] - [[package]] name = "thiserror" version = "1.0.61" @@ -3033,9 +3021,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "pin-project-lite", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "tracing-core" version = "0.1.32" @@ -3043,6 +3043,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", ] [[package]] @@ -3142,6 +3172,12 @@ dependencies = [ "serde", ] +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "vcpkg" version = "0.2.15" diff --git a/Cargo.toml b/Cargo.toml index 746daaf..607ec65 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,13 +18,16 @@ anyhow = "1" cadence = "1" crates-index = { version = "2", default-features = false, features = ["git"] } derive_more = "0.99" +dotenvy = "0.15" font-awesome-as-a-crate = "0.3" futures-util = { version = "0.3", default-features = false, features = ["std"] } hyper = { version = "0.14.10", features = ["full"] } +error_reporter = "1" indexmap = { version = "2", features = ["serde"] } lru_time_cache = "0.11" maud = "0.26" once_cell = "1" +parking_lot = "0.12" pulldown-cmark = "0.11" relative-path = { version = "1", features = ["serde"] } reqwest = { version = "0.12", features = ["json"] } @@ -33,11 +36,10 @@ rustsec = "0.29" semver = { version = "1.0", features = ["serde"] } serde = { version = "1", features = ["derive"] } serde_urlencoded = "0.7" -slog = "2" -slog-async = "2" -slog-term = "2" tokio = { version = "1.24.2", features = ["rt-multi-thread", "macros", "sync", "time"] } toml = "0.8" +tracing = "0.1.30" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } [target.'cfg(any())'.dependencies] gix = { version = "0.63", default-features = false, features = ["blocking-http-transport-reqwest-rust-tls"] } diff --git a/build.rs b/build.rs index 6ac4147..f8e3f0d 100644 --- a/build.rs +++ b/build.rs @@ -1,6 +1,4 @@ -use std::env; -use std::fs; -use std::path::Path; +use std::{env, fs, path::Path}; use sha1::{Digest, Sha1}; diff --git a/libs/badge/badge.rs b/libs/badge/badge.rs index 146eb69..f328738 100644 --- a/libs/badge/badge.rs +++ b/libs/badge/badge.rs @@ -235,8 +235,8 @@ mod tests { #[test] #[ignore] fn test_to_svg() { - use std::fs::File; - use std::io::Write; + use std::{fs::File, io::Write as _}; + let mut file = File::create("test.svg").unwrap(); let options = BadgeOptions { subject: "latest".to_owned(), diff --git a/src/engine/fut/crawl.rs b/src/engine/fut/crawl.rs index 08165dc..c30d88a 100644 --- a/src/engine/fut/crawl.rs +++ b/src/engine/fut/crawl.rs @@ -2,11 +2,12 @@ use anyhow::Error; use futures_util::{future::BoxFuture, stream::FuturesOrdered, FutureExt as _, StreamExt as _}; use relative_path::RelativePathBuf; -use crate::models::repo::RepoPath; - -use crate::engine::{ - machines::crawler::{ManifestCrawler, ManifestCrawlerOutput}, - Engine, +use crate::{ + engine::{ + machines::crawler::{ManifestCrawler, ManifestCrawlerOutput}, + Engine, + }, + models::repo::RepoPath, }; pub async fn crawl_manifest( diff --git a/src/engine/fut/mod.rs b/src/engine/fut/mod.rs index 9112e18..800118d 100644 --- a/src/engine/fut/mod.rs +++ b/src/engine/fut/mod.rs @@ -1,5 +1,4 @@ mod analyze; mod crawl; -pub use self::analyze::analyze_dependencies; -pub use self::crawl::crawl_manifest; +pub use self::{analyze::analyze_dependencies, crawl::crawl_manifest}; diff --git a/src/engine/machines/analyzer.rs b/src/engine/machines/analyzer.rs index 8ebfb01..4e607c9 100644 --- a/src/engine/machines/analyzer.rs +++ b/src/engine/machines/analyzer.rs @@ -101,9 +101,8 @@ impl DependencyAnalyzer { #[cfg(test)] mod tests { - use crate::models::crates::{CrateDep, CrateDeps, CrateRelease}; - use super::*; + use crate::models::crates::{CrateDep, CrateDeps, CrateRelease}; #[test] fn tracks_latest_without_matching() { diff --git a/src/engine/machines/crawler.rs b/src/engine/machines/crawler.rs index 8eb7ba4..267db8d 100644 --- a/src/engine/machines/crawler.rs +++ b/src/engine/machines/crawler.rs @@ -4,8 +4,10 @@ use anyhow::Error; use indexmap::IndexMap; use relative_path::RelativePathBuf; -use crate::models::crates::{CrateDep, CrateDeps, CrateManifest, CrateName}; -use crate::parsers::manifest::parse_manifest_toml; +use crate::{ + models::crates::{CrateDep, CrateDeps, CrateManifest, CrateName}, + parsers::manifest::parse_manifest_toml, +}; pub struct ManifestCrawlerOutput { pub crates: IndexMap, @@ -118,9 +120,8 @@ mod tests { use relative_path::RelativePath; use semver::VersionReq; - use crate::models::crates::CrateDep; - use super::*; + use crate::models::crates::CrateDep; #[test] fn simple_package_manifest() { diff --git a/src/engine/mod.rs b/src/engine/mod.rs index 2603ab3..34e89ea 100644 --- a/src/engine/mod.rs +++ b/src/engine/mod.rs @@ -7,7 +7,6 @@ use std::{ use anyhow::{anyhow, Error}; use cadence::{MetricSink, NopMetricSink, StatsdClient}; - use futures_util::{ future::try_join_all, stream::{self, BoxStream}, @@ -18,16 +17,21 @@ use once_cell::sync::Lazy; use relative_path::{RelativePath, RelativePathBuf}; use rustsec::database::Database; use semver::VersionReq; -use slog::Logger; -use crate::interactors::crates::{GetPopularCrates, QueryCrate}; -use crate::interactors::github::GetPopularRepos; -use crate::interactors::rustsec::FetchAdvisoryDatabase; -use crate::interactors::RetrieveFileAtPath; -use crate::models::crates::{AnalyzedDependencies, CrateName, CratePath, CrateRelease}; -use crate::models::repo::{RepoPath, Repository}; -use crate::utils::cache::Cache; -use crate::ManagedIndex; +use crate::{ + interactors::{ + crates::{GetPopularCrates, QueryCrate}, + github::GetPopularRepos, + rustsec::FetchAdvisoryDatabase, + RetrieveFileAtPath, + }, + models::{ + crates::{AnalyzedDependencies, CrateName, CratePath, CrateRelease}, + repo::{RepoPath, Repository}, + }, + utils::cache::Cache, + ManagedIndex, +}; mod fut; mod machines; @@ -45,33 +49,25 @@ pub struct Engine { } impl Engine { - pub fn new(client: reqwest::Client, index: ManagedIndex, logger: Logger) -> Engine { + pub fn new(client: reqwest::Client, index: ManagedIndex) -> Engine { let metrics = Arc::new(StatsdClient::from_sink("engine", NopMetricSink)); - let query_crate = Cache::new( - QueryCrate::new(index), - Duration::from_secs(10), - 500, - logger.clone(), - ); + let query_crate = Cache::new(QueryCrate::new(index), Duration::from_secs(10), 500); let get_popular_crates = Cache::new( GetPopularCrates::new(client.clone()), Duration::from_secs(15 * 60), 1, - logger.clone(), ); let get_popular_repos = Cache::new( GetPopularRepos::new(client.clone()), Duration::from_secs(5 * 60), 1, - logger.clone(), ); let retrieve_file_at_path = RetrieveFileAtPath::new(client.clone()); let fetch_advisory_db = Cache::new( FetchAdvisoryDatabase::new(client), Duration::from_secs(30 * 60), 1, - logger, ); Engine { diff --git a/src/interactors/crates.rs b/src/interactors/crates.rs index e490a64..1a07f5c 100644 --- a/src/interactors/crates.rs +++ b/src/interactors/crates.rs @@ -1,4 +1,7 @@ -use std::{fmt, str, task::Context, task::Poll}; +use std::{ + fmt, str, + task::{Context, Poll}, +}; use anyhow::{anyhow, Error}; use crates_index::{Crate, DependencyKind}; @@ -6,7 +9,6 @@ use futures_util::FutureExt as _; use hyper::service::Service; use semver::{Version, VersionReq}; use serde::Deserialize; - use tokio::task::spawn_blocking; use crate::{ diff --git a/src/interactors/github.rs b/src/interactors/github.rs index 32d5c86..72bb684 100644 --- a/src/interactors/github.rs +++ b/src/interactors/github.rs @@ -4,7 +4,6 @@ use std::{ }; use anyhow::Error; - use futures_util::FutureExt as _; use hyper::service::Service; use serde::Deserialize; diff --git a/src/interactors/rustsec.rs b/src/interactors/rustsec.rs index e28488e..46ddd55 100644 --- a/src/interactors/rustsec.rs +++ b/src/interactors/rustsec.rs @@ -1,4 +1,8 @@ -use std::{fmt, sync::Arc, task::Context, task::Poll}; +use std::{ + fmt, + sync::Arc, + task::{Context, Poll}, +}; use anyhow::Error; use futures_util::FutureExt as _; diff --git a/src/main.rs b/src/main.rs index c568f0b..c5414ac 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,9 +15,8 @@ use hyper::{ service::{make_service_fn, service_fn}, Server, }; - use reqwest::redirect::Policy as RedirectPolicy; -use slog::{error, info, o, Drain, Logger}; +use tracing::Instrument as _; mod engine; mod interactors; @@ -26,9 +25,7 @@ mod parsers; mod server; mod utils; -use self::engine::Engine; -use self::server::App; -use self::utils::index::ManagedIndex; +use self::{engine::Engine, server::App, utils::index::ManagedIndex}; /// Future crate's BoxFuture without the explicit lifetime parameter. pub type BoxFuture = Pin + Send>>; @@ -43,18 +40,29 @@ fn init_metrics() -> QueuingMetricSink { QueuingMetricSink::from(sink) } -fn init_root_logger() -> Logger { - let decorator = slog_term::TermDecorator::new().build(); - let drain = slog_term::FullFormat::new(decorator).build().fuse(); - let drain = slog_async::Async::new(drain).build().fuse(); - - Logger::root(drain, o!()) +fn init_tracing_subscriber() { + use tracing::level_filters::LevelFilter; + use tracing_subscriber::{fmt, prelude::*, EnvFilter}; + + let stdout_logger = match env::var("RUST_LOG_TIME").as_deref() { + Ok("false") => fmt::layer().without_time().boxed(), + _ => fmt::layer().boxed(), + }; + + tracing_subscriber::registry() + .with( + EnvFilter::builder() + .with_default_directive(LevelFilter::INFO.into()) + .from_env_lossy(), + ) + .with(stdout_logger) + .init(); } #[tokio::main] async fn main() { - let logger = init_root_logger(); - + dotenvy::dotenv().ok(); + init_tracing_subscriber(); let metrics = init_metrics(); let client = reqwest::Client::builder() @@ -71,7 +79,7 @@ async fn main() { let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), port); - let index = ManagedIndex::new(logger.clone()); + let index = ManagedIndex::new(); { let index = index.clone(); @@ -81,27 +89,32 @@ async fn main() { }); } - let mut engine = Engine::new(client.clone(), index, logger.new(o!())); + let mut engine = Engine::new(client.clone(), index); engine.set_metrics(metrics); - let svc_logger = logger.new(o!()); let make_svc = make_service_fn(move |_socket: &AddrStream| { let engine = engine.clone(); - let logger = svc_logger.clone(); async move { - let server = App::new(logger.clone(), engine.clone()); + let server = App::new(engine.clone()); Ok::<_, hyper::Error>(service_fn(move |req| { let server = server.clone(); - async move { server.handle(req).await } + async move { + let path = req.uri().path().to_owned(); + + server + .handle(req) + .instrument(tracing::info_span!("@", %path)) + .await + } })) } }); let server = Server::bind(&addr).serve(make_svc); - info!(logger, "Server running on port {}", port); + tracing::info!("Server running on port {port}"); - if let Err(e) = server.await { - error!(logger, "server error: {}", e); + if let Err(err) = server.await { + tracing::error!("server error: {err}"); } } diff --git a/src/parsers/manifest.rs b/src/parsers/manifest.rs index 64535fd..c461050 100644 --- a/src/parsers/manifest.rs +++ b/src/parsers/manifest.rs @@ -135,9 +135,8 @@ pub fn parse_manifest_toml(input: &str) -> Result { #[cfg(test)] mod tests { - use crate::models::crates::CrateManifest; - use super::*; + use crate::models::crates::CrateManifest; #[test] fn parse_workspace_without_members_declaration() { diff --git a/src/server/mod.rs b/src/server/mod.rs index ea54c19..3355066 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -10,7 +10,6 @@ use once_cell::sync::Lazy; use route_recognizer::{Params, Router}; use semver::VersionReq; use serde::Deserialize; -use slog::{error, info, o, Logger}; mod assets; mod views; @@ -18,10 +17,14 @@ mod views; use self::assets::{ STATIC_LINKS_JS_ETAG, STATIC_LINKS_JS_PATH, STATIC_STYLE_CSS_ETAG, STATIC_STYLE_CSS_PATH, }; -use crate::engine::{AnalyzeDependenciesOutcome, Engine}; -use crate::models::crates::{CrateName, CratePath}; -use crate::models::repo::RepoPath; -use crate::models::SubjectPath; +use crate::{ + engine::{AnalyzeDependenciesOutcome, Engine}, + models::{ + crates::{CrateName, CratePath}, + repo::RepoPath, + SubjectPath, + }, +}; #[derive(Debug, Clone, Copy, PartialEq)] enum StatusFormat { @@ -47,13 +50,12 @@ enum Route { #[derive(Clone)] pub struct App { - logger: Logger, engine: Engine, router: Arc>, } impl App { - pub fn new(logger: Logger, engine: Engine) -> App { + pub fn new(engine: Engine) -> App { let mut router = Router::new(); router.add("/", Route::Index); @@ -83,15 +85,12 @@ impl App { ); App { - logger, engine, router: Arc::new(router), } } pub async fn handle(&self, req: Request) -> Result, HyperError> { - let logger = self.logger.new(o!("path" => req.uri().path().to_owned())); - let logger2 = logger.clone(); let start = Instant::now(); // allows `/path/` to also match `/path` @@ -99,28 +98,25 @@ impl App { let res = if let Ok(route_match) = self.router.recognize(normalized_path) { match (req.method(), route_match.handler()) { - (&Method::GET, Route::Index) => { - self.index(req, route_match.params().clone(), logger).await - } + (&Method::GET, Route::Index) => self.index(req, route_match.params().clone()).await, (&Method::GET, Route::RepoStatus(format)) => { - self.repo_status(req, route_match.params().clone(), logger, *format) + self.repo_status(req, route_match.params().clone(), *format) .await } (&Method::GET, Route::CrateStatus(format)) => { - self.crate_status(req, route_match.params().clone(), logger, *format) + self.crate_status(req, route_match.params().clone(), *format) .await } (&Method::GET, Route::LatestCrateBadge) => { - self.crate_status(req, route_match.params().clone(), logger, StatusFormat::Svg) + self.crate_status(req, route_match.params().clone(), StatusFormat::Svg) .await } (&Method::GET, Route::CrateRedirect) => { - self.crate_redirect(req, route_match.params().clone(), logger) - .await + self.crate_redirect(req, route_match.params().clone()).await } (&Method::GET, Route::Static(file)) => Ok(App::static_file(*file)), @@ -135,12 +131,11 @@ impl App { let diff = end - start; match &res { - Ok(res) => info!( - logger2, ""; - "status" => res.status().to_string(), - "time" => format!("{}ms", diff.as_millis()) + Ok(res) => tracing::info!( + status = %res.status(), + time = %format_args!("{}ms", diff.as_millis()), ), - Err(err) => error!(logger2, ""; "error" => err.to_string()), + Err(err) => tracing::error!(%err), }; res @@ -152,7 +147,6 @@ impl App { &self, _req: Request, _params: Params, - logger: Logger, ) -> Result, HyperError> { let engine = self.engine.clone(); @@ -161,7 +155,7 @@ impl App { match popular { Err(err) => { - error!(logger, "error: {}", err); + tracing::error!(%err); let mut response = views::html::error::render("Could not retrieve popular items", ""); *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; @@ -177,7 +171,6 @@ impl App { &self, req: Request, params: Params, - logger: Logger, format: StatusFormat, ) -> Result, HyperError> { let server = self.clone(); @@ -192,7 +185,7 @@ impl App { match repo_path_result { Err(err) => { - error!(logger, "error: {}", err); + tracing::error!(%err); let mut response = views::html::error::render( "Could not parse repository path", "Please make sure to provide a valid repository path.", @@ -209,7 +202,7 @@ impl App { match analyze_result { Err(err) => { - error!(logger, "error: {}", err); + tracing::error!(%err); let response = App::status_format_analysis( None, format, @@ -236,7 +229,6 @@ impl App { &self, _req: Request, params: Params, - logger: Logger, ) -> Result, HyperError> { let engine = self.engine.clone(); @@ -245,7 +237,7 @@ impl App { match crate_name_result { Err(err) => { - error!(logger, "error: {}", err); + tracing::error!(%err); let mut response = views::html::error::render( "Could not parse crate name", "Please make sure to provide a valid crate name.", @@ -261,7 +253,7 @@ impl App { match release_result { Err(err) => { - error!(logger, "error: {}", err); + tracing::error!(%err); let mut response = views::html::error::render( "Could not fetch crate information", "Please make sure to provide a valid crate name.", @@ -302,7 +294,6 @@ impl App { &self, req: Request, params: Params, - logger: Logger, format: StatusFormat, ) -> Result, HyperError> { let server = self.clone(); @@ -332,7 +323,7 @@ impl App { Ok(Some(latest_rel)) => latest_rel.version.to_string(), Ok(None) => return Ok(not_found()), Err(err) => { - error!(logger, "error: {}", err); + tracing::error!(%err); let mut response = views::html::error::render( "Could not fetch crate information", "Please make sure to provide a valid crate name.", @@ -349,7 +340,7 @@ impl App { match crate_path_result { Err(err) => { - error!(logger, "error: {}", err); + tracing::error!(%err); let mut response = views::html::error::render( "Could not parse crate path", "Please make sure to provide a valid crate name and version.", @@ -365,7 +356,7 @@ impl App { match analyze_result { Err(err) => { - error!(logger, "error: {}", err); + tracing::error!(%err); let response = App::status_format_analysis( None, format, diff --git a/src/server/views/badge.rs b/src/server/views/badge.rs index 0ee55c5..beba072 100644 --- a/src/server/views/badge.rs +++ b/src/server/views/badge.rs @@ -1,9 +1,7 @@ use badge::{Badge, BadgeOptions}; -use hyper::header::CONTENT_TYPE; -use hyper::{Body, Response}; +use hyper::{header::CONTENT_TYPE, Body, Response}; -use crate::engine::AnalyzeDependenciesOutcome; -use crate::server::ExtraConfig; +use crate::{engine::AnalyzeDependenciesOutcome, server::ExtraConfig}; pub fn badge( analysis_outcome: Option<&AnalyzeDependenciesOutcome>, diff --git a/src/server/views/html/index.rs b/src/server/views/html/index.rs index a1ccae0..748eca0 100644 --- a/src/server/views/html/index.rs +++ b/src/server/views/html/index.rs @@ -1,10 +1,10 @@ use hyper::{Body, Response}; use maud::{html, Markup}; -use crate::models::crates::CratePath; -use crate::models::repo::Repository; - -use crate::server::assets::STATIC_LINKS_JS_PATH; +use crate::{ + models::{crates::CratePath, repo::Repository}, + server::assets::STATIC_LINKS_JS_PATH, +}; fn link_forms() -> Markup { html! { diff --git a/src/server/views/html/mod.rs b/src/server/views/html/mod.rs index 2f3457b..844eb4a 100644 --- a/src/server/views/html/mod.rs +++ b/src/server/views/html/mod.rs @@ -1,15 +1,13 @@ use std::time::Duration; -use hyper::header::CONTENT_TYPE; -use hyper::{Body, Response}; +use hyper::{header::CONTENT_TYPE, Body, Response}; use maud::{html, Markup, Render, DOCTYPE}; pub mod error; pub mod index; pub mod status; -use crate::server::assets::STATIC_STYLE_CSS_PATH; -use crate::server::SELF_BASE_URL; +use crate::server::{assets::STATIC_STYLE_CSS_PATH, SELF_BASE_URL}; fn render_html(title: &str, body: B) -> Response { let rendered = html! { diff --git a/src/server/views/html/status.rs b/src/server/views/html/status.rs index 19c0d23..8017e94 100644 --- a/src/server/views/html/status.rs +++ b/src/server/views/html/status.rs @@ -6,12 +6,15 @@ use pulldown_cmark::{html, Parser}; use rustsec::advisory::Advisory; use semver::Version; -use crate::engine::AnalyzeDependenciesOutcome; -use crate::models::crates::{AnalyzedDependencies, AnalyzedDependency, CrateName}; -use crate::models::repo::RepoSite; -use crate::models::SubjectPath; -use crate::server::views::badge; -use crate::server::ExtraConfig; +use crate::{ + engine::AnalyzeDependenciesOutcome, + models::{ + crates::{AnalyzedDependencies, AnalyzedDependency, CrateName}, + repo::RepoSite, + SubjectPath, + }, + server::{views::badge, ExtraConfig}, +}; fn get_crates_url(name: impl AsRef) -> String { format!("https://crates.io/crates/{}", name.as_ref()) diff --git a/src/utils/cache.rs b/src/utils/cache.rs index 5637472..a09c706 100644 --- a/src/utils/cache.rs +++ b/src/utils/cache.rs @@ -3,7 +3,6 @@ use std::{fmt, sync::Arc, time::Duration}; use derive_more::{Display, Error, From}; use hyper::service::Service; use lru_time_cache::LruCache; -use slog::{debug, Logger}; use tokio::sync::Mutex; #[derive(Debug, Clone, Display, From, Error)] @@ -18,7 +17,6 @@ where { inner: S, cache: Arc>>, - logger: Logger, } impl fmt::Debug for Cache @@ -38,13 +36,12 @@ where S::Response: Clone, Req: Clone + Eq + Ord + fmt::Debug, { - pub fn new(service: S, ttl: Duration, capacity: usize, logger: Logger) -> Cache { + pub fn new(service: S, ttl: Duration, capacity: usize) -> Cache { let cache = LruCache::with_expiry_duration_and_capacity(ttl, capacity); Cache { inner: service, cache: Arc::new(Mutex::new(cache)), - logger, } } @@ -53,19 +50,19 @@ where let mut cache = self.cache.lock().await; if let Some(cached_response) = cache.get(&req) { - debug!( - self.logger, "cache hit"; - "svc" => format!("{:?}", self.inner), - "req" => format!("{:?}", &req) + tracing::debug!( + svc = ?self.inner, + req = ?req, + cache = "hit", ); return Ok(cached_response.clone()); } } - debug!( - self.logger, "cache miss"; - "svc" => format!("{:?}", self.inner), - "req" => format!("{:?}", &req) + tracing::debug!( + svc = ?self.inner, + req = ?req, + cache = "miss", ); let mut service = self.inner.clone(); diff --git a/src/utils/index.rs b/src/utils/index.rs index d2f2587..4e58ae3 100644 --- a/src/utils/index.rs +++ b/src/utils/index.rs @@ -1,32 +1,30 @@ -use std::sync::Arc; -use std::sync::Mutex; -use std::time::Duration; +use std::{sync::Arc, time::Duration}; -use crate::models::crates::CrateName; use anyhow::Result; -use crates_index::Crate; -use crates_index::GitIndex; -use slog::{error, Logger}; -use tokio::task::spawn_blocking; -use tokio::time::{self, MissedTickBehavior}; +use crates_index::{Crate, GitIndex}; +use parking_lot::Mutex; +use tokio::{ + task::spawn_blocking, + time::{self, MissedTickBehavior}, +}; + +use crate::models::crates::CrateName; #[derive(Clone)] pub struct ManagedIndex { index: Arc>, - logger: Logger, } impl ManagedIndex { - pub fn new(logger: Logger) -> Self { + pub fn new() -> Self { // the index path is configurable through the `CARGO_HOME` env variable let index = Arc::new(Mutex::new(GitIndex::new_cargo_default().unwrap())); - Self { index, logger } + + Self { index } } pub fn crate_(&self, crate_name: CrateName) -> Option { - let index = self.index.lock().unwrap(); - - index.crate_(crate_name.as_ref()) + self.index.lock().crate_(crate_name.as_ref()) } pub async fn refresh_at_interval(&self, update_interval: Duration) { @@ -34,25 +32,23 @@ impl ManagedIndex { update_interval.set_missed_tick_behavior(MissedTickBehavior::Delay); loop { - if let Err(e) = self.refresh().await { - error!( - self.logger, - "failed refreshing the crates.io-index, the operation will be retried: {}", e + if let Err(err) = self.refresh().await { + tracing::error!( + "failed refreshing the crates.io-index, the operation will be retried: {}", + error_reporter::Report::new(err), ); } update_interval.tick().await; } } - async fn refresh(&self) -> Result<()> { + async fn refresh(&self) -> Result<(), crates_index::Error> { let index = Arc::clone(&self.index); - spawn_blocking(move || { - let mut index = index.lock().unwrap(); + spawn_blocking(move || index.lock().update()) + .await + .expect("blocking index update task should never panic")?; - index.update() - }) - .await??; Ok(()) } } From d4d0db2e1ea603d2a389f5aa0c41607cb7a9b14d Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Mon, 27 May 2024 03:50:24 +0100 Subject: [PATCH 2/3] refactor: migrate web server to axum (lite) --- Cargo.lock | 203 +++++++++++++++++++------------- Cargo.toml | 3 +- src/engine/mod.rs | 2 +- src/interactors/crates.rs | 2 +- src/interactors/github.rs | 2 +- src/interactors/mod.rs | 2 +- src/interactors/rustsec.rs | 2 +- src/main.rs | 37 +++--- src/server/mod.rs | 23 ++-- src/server/views/badge.rs | 2 +- src/server/views/html/error.rs | 10 +- src/server/views/html/index.rs | 2 +- src/server/views/html/mod.rs | 2 +- src/server/views/html/status.rs | 2 +- src/utils/cache.rs | 2 +- 15 files changed, 165 insertions(+), 131 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 66a9d16..4811221 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -81,6 +81,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "async-trait" +version = "0.1.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -93,6 +104,61 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +[[package]] +name = "axum" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +dependencies = [ + "async-trait", + "axum-core", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper 1.0.1", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper 0.1.2", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "backtrace" version = "0.3.71" @@ -1390,25 +1456,6 @@ dependencies = [ "phf", ] -[[package]] -name = "h2" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http 0.2.12", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - [[package]] name = "h2" version = "0.4.5" @@ -1420,7 +1467,7 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http 1.1.0", + "http", "indexmap", "slab", "tokio", @@ -1471,17 +1518,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - [[package]] name = "http" version = "1.1.0" @@ -1493,17 +1529,6 @@ dependencies = [ "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]] name = "http-body" version = "1.0.0" @@ -1511,7 +1536,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" dependencies = [ "bytes", - "http 1.1.0", + "http", ] [[package]] @@ -1522,8 +1547,8 @@ checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" dependencies = [ "bytes", "futures-core", - "http 1.1.0", - "http-body 1.0.0", + "http", + "http-body", "pin-project-lite", ] @@ -1539,30 +1564,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "hyper" version = "1.3.1" @@ -1572,10 +1573,11 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.5", - "http 1.1.0", - "http-body 1.0.0", + "h2", + "http", + "http-body", "httparse", + "httpdate", "itoa", "pin-project-lite", "smallvec", @@ -1590,8 +1592,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" dependencies = [ "futures-util", - "http 1.1.0", - "hyper 1.3.1", + "http", + "hyper", "hyper-util", "rustls", "rustls-pki-types", @@ -1608,7 +1610,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.3.1", + "hyper", "hyper-util", "native-tls", "tokio", @@ -1625,9 +1627,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.1.0", - "http-body 1.0.0", - "hyper 1.3.1", + "http", + "http-body", + "hyper", "pin-project-lite", "socket2", "tokio", @@ -1765,6 +1767,12 @@ dependencies = [ "regex-automata 0.1.10", ] +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + [[package]] name = "maud" version = "0.26.0" @@ -2275,11 +2283,11 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2 0.4.5", - "http 1.1.0", - "http-body 1.0.0", + "h2", + "http", + "http-body", "http-body-util", - "hyper 1.3.1", + "hyper", "hyper-rustls", "hyper-tls", "hyper-util", @@ -2298,7 +2306,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 0.1.2", "system-configuration", "tokio", "tokio-native-tls", @@ -2453,6 +2461,12 @@ dependencies = [ "owned_ttf_parser", ] +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + [[package]] name = "ryu" version = "1.0.18" @@ -2546,6 +2560,16 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +dependencies = [ + "itoa", + "serde", +] + [[package]] name = "serde_spanned" version = "0.6.6" @@ -2604,6 +2628,7 @@ name = "shiny-robots" version = "0.1.0" dependencies = [ "anyhow", + "axum", "badge", "cadence", "crates-index", @@ -2614,7 +2639,6 @@ dependencies = [ "futures-util", "gix", "grass", - "hyper 0.14.28", "indexmap", "lru_time_cache", "maud", @@ -2631,6 +2655,7 @@ dependencies = [ "sha-1", "tokio", "toml 0.8.13", + "tower", "tracing", "tracing-subscriber", ] @@ -2721,6 +2746,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" + [[package]] name = "system-configuration" version = "0.5.1" @@ -2752,7 +2783,7 @@ dependencies = [ "crossbeam-channel", "gix", "home", - "http 1.1.0", + "http", "libc", "memchr", "rayon", @@ -3000,6 +3031,7 @@ dependencies = [ "tokio", "tower-layer", "tower-service", + "tracing", ] [[package]] @@ -3020,6 +3052,7 @@ version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ + "log", "pin-project-lite", "tracing-attributes", "tracing-core", diff --git a/Cargo.toml b/Cargo.toml index 607ec65..9eb97ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,13 +15,13 @@ edition = "2021" badge = { path = "./libs/badge" } anyhow = "1" +axum = "0.7" cadence = "1" crates-index = { version = "2", default-features = false, features = ["git"] } derive_more = "0.99" dotenvy = "0.15" font-awesome-as-a-crate = "0.3" futures-util = { version = "0.3", default-features = false, features = ["std"] } -hyper = { version = "0.14.10", features = ["full"] } error_reporter = "1" indexmap = { version = "2", features = ["serde"] } lru_time_cache = "0.11" @@ -38,6 +38,7 @@ serde = { version = "1", features = ["derive"] } serde_urlencoded = "0.7" tokio = { version = "1.24.2", features = ["rt-multi-thread", "macros", "sync", "time"] } toml = "0.8" +tower = "0.4" tracing = "0.1.30" tracing-subscriber = { version = "0.3", features = ["env-filter"] } diff --git a/src/engine/mod.rs b/src/engine/mod.rs index 34e89ea..9fd9cef 100644 --- a/src/engine/mod.rs +++ b/src/engine/mod.rs @@ -12,11 +12,11 @@ use futures_util::{ stream::{self, BoxStream}, StreamExt as _, }; -use hyper::service::Service; use once_cell::sync::Lazy; use relative_path::{RelativePath, RelativePathBuf}; use rustsec::database::Database; use semver::VersionReq; +use tower::Service; use crate::{ interactors::{ diff --git a/src/interactors/crates.rs b/src/interactors/crates.rs index 1a07f5c..956a053 100644 --- a/src/interactors/crates.rs +++ b/src/interactors/crates.rs @@ -6,10 +6,10 @@ use std::{ use anyhow::{anyhow, Error}; use crates_index::{Crate, DependencyKind}; use futures_util::FutureExt as _; -use hyper::service::Service; use semver::{Version, VersionReq}; use serde::Deserialize; use tokio::task::spawn_blocking; +use tower::Service; use crate::{ models::crates::{CrateDep, CrateDeps, CrateName, CratePath, CrateRelease}, diff --git a/src/interactors/github.rs b/src/interactors/github.rs index 72bb684..34a7bc1 100644 --- a/src/interactors/github.rs +++ b/src/interactors/github.rs @@ -5,8 +5,8 @@ use std::{ use anyhow::Error; use futures_util::FutureExt as _; -use hyper::service::Service; use serde::Deserialize; +use tower::Service; use crate::{ models::repo::{RepoPath, Repository}, diff --git a/src/interactors/mod.rs b/src/interactors/mod.rs index 030174f..b6291cf 100644 --- a/src/interactors/mod.rs +++ b/src/interactors/mod.rs @@ -5,8 +5,8 @@ use std::{ use anyhow::{anyhow, Error}; use futures_util::FutureExt as _; -use hyper::service::Service; use relative_path::RelativePathBuf; +use tower::Service; use crate::{models::repo::RepoPath, BoxFuture}; diff --git a/src/interactors/rustsec.rs b/src/interactors/rustsec.rs index 46ddd55..e65887d 100644 --- a/src/interactors/rustsec.rs +++ b/src/interactors/rustsec.rs @@ -6,8 +6,8 @@ use std::{ use anyhow::Error; use futures_util::FutureExt as _; -use hyper::service::Service; use rustsec::database::Database; +use tower::Service; use crate::BoxFuture; diff --git a/src/main.rs b/src/main.rs index c5414ac..ebf76f6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,12 +9,8 @@ use std::{ time::Duration, }; +use axum::{extract::Request, Router}; use cadence::{QueuingMetricSink, UdpMetricSink}; -use hyper::{ - server::conn::AddrStream, - service::{make_service_fn, service_fn}, - Server, -}; use reqwest::redirect::Policy as RedirectPolicy; use tracing::Instrument as _; @@ -92,25 +88,20 @@ async fn main() { let mut engine = Engine::new(client.clone(), index); engine.set_metrics(metrics); - let make_svc = make_service_fn(move |_socket: &AddrStream| { - let engine = engine.clone(); - - async move { - 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 - .handle(req) - .instrument(tracing::info_span!("@", %path)) - .await - } - })) - } + let app = App::new(engine.clone()); + + let lst = tokio::net::TcpListener::bind(addr).await.unwrap(); + + let router = Router::new().fallback(|req: Request| async move { + let path = req.uri().path().to_owned(); + + app.handle(req) + .instrument(tracing::info_span!("@", %path)) + .await + .unwrap() }); - let server = Server::bind(&addr).serve(make_svc); + + let server = axum::serve(lst, router); tracing::info!("Server running on port {port}"); diff --git a/src/server/mod.rs b/src/server/mod.rs index 3355066..2bd902c 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,11 +1,16 @@ use std::{env, sync::Arc, time::Instant}; +use axum::{ + body::Body, + extract::Request, + http::{ + header::{CACHE_CONTROL, CONTENT_TYPE, ETAG, LOCATION}, + Method, StatusCode, + }, + response::Response, +}; use badge::BadgeStyle; 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 route_recognizer::{Params, Router}; use semver::VersionReq; @@ -90,7 +95,7 @@ impl App { } } - pub async fn handle(&self, req: Request) -> Result, HyperError> { + pub async fn handle(&self, req: Request) -> Result, axum::Error> { let start = Instant::now(); // allows `/path/` to also match `/path` @@ -147,7 +152,7 @@ impl App { &self, _req: Request, _params: Params, - ) -> Result, HyperError> { + ) -> Result, axum::Error> { let engine = self.engine.clone(); let popular = @@ -172,7 +177,7 @@ impl App { req: Request, params: Params, format: StatusFormat, - ) -> Result, HyperError> { + ) -> Result, axum::Error> { let server = self.clone(); let site = params.find("site").expect("route param 'site' not found"); @@ -229,7 +234,7 @@ impl App { &self, _req: Request, params: Params, - ) -> Result, HyperError> { + ) -> Result, axum::Error> { let engine = self.engine.clone(); let name = params.find("name").expect("route param 'name' not found"); @@ -295,7 +300,7 @@ impl App { req: Request, params: Params, format: StatusFormat, - ) -> Result, HyperError> { + ) -> Result, axum::Error> { let server = self.clone(); let name = params.find("name").expect("route param 'name' not found"); diff --git a/src/server/views/badge.rs b/src/server/views/badge.rs index beba072..a30819f 100644 --- a/src/server/views/badge.rs +++ b/src/server/views/badge.rs @@ -1,5 +1,5 @@ +use axum::{body::Body, http::header::CONTENT_TYPE, response::Response}; use badge::{Badge, BadgeOptions}; -use hyper::{header::CONTENT_TYPE, Body, Response}; use crate::{engine::AnalyzeDependenciesOutcome, server::ExtraConfig}; diff --git a/src/server/views/html/error.rs b/src/server/views/html/error.rs index f5802e2..dfab2ee 100644 --- a/src/server/views/html/error.rs +++ b/src/server/views/html/error.rs @@ -1,6 +1,10 @@ -use hyper::{ - header::{CACHE_CONTROL, CONTENT_TYPE}, - Body, Response, StatusCode, +use axum::{ + body::Body, + http::{ + header::{CACHE_CONTROL, CONTENT_TYPE}, + StatusCode, + }, + response::Response, }; use maud::html; diff --git a/src/server/views/html/index.rs b/src/server/views/html/index.rs index 748eca0..83c79fd 100644 --- a/src/server/views/html/index.rs +++ b/src/server/views/html/index.rs @@ -1,4 +1,4 @@ -use hyper::{Body, Response}; +use axum::{body::Body, response::Response}; use maud::{html, Markup}; use crate::{ diff --git a/src/server/views/html/mod.rs b/src/server/views/html/mod.rs index 844eb4a..a4d525c 100644 --- a/src/server/views/html/mod.rs +++ b/src/server/views/html/mod.rs @@ -1,6 +1,6 @@ use std::time::Duration; -use hyper::{header::CONTENT_TYPE, Body, Response}; +use axum::{body::Body, http::header::CONTENT_TYPE, response::Response}; use maud::{html, Markup, Render, DOCTYPE}; pub mod error; diff --git a/src/server/views/html/status.rs b/src/server/views/html/status.rs index 8017e94..ac0b3de 100644 --- a/src/server/views/html/status.rs +++ b/src/server/views/html/status.rs @@ -1,5 +1,5 @@ +use axum::{body::Body, response::Response}; use font_awesome_as_a_crate::{svg as fa, Type as FaType}; -use hyper::{Body, Response}; use indexmap::IndexMap; use maud::{html, Markup, PreEscaped}; use pulldown_cmark::{html, Parser}; diff --git a/src/utils/cache.rs b/src/utils/cache.rs index a09c706..7408b00 100644 --- a/src/utils/cache.rs +++ b/src/utils/cache.rs @@ -1,9 +1,9 @@ use std::{fmt, sync::Arc, time::Duration}; use derive_more::{Display, Error, From}; -use hyper::service::Service; use lru_time_cache::LruCache; use tokio::sync::Mutex; +use tower::Service; #[derive(Debug, Clone, Display, From, Error)] pub struct CacheError { From 3dfb480ba40c966beab6e6b825b66f1aecc86b14 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Mon, 27 May 2024 03:48:30 +0100 Subject: [PATCH 3/3] feat: migrate to axum router --- Cargo.lock | 25 +++- Cargo.toml | 2 +- src/main.rs | 17 +-- src/server/assets.rs | 2 + src/server/mod.rs | 318 +++++++++++++++++++------------------------ 5 files changed, 164 insertions(+), 200 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4811221..4b98ecc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2336,12 +2336,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "route-recognizer" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746" - [[package]] name = "rustc-demangle" version = "0.1.24" @@ -2647,7 +2641,6 @@ dependencies = [ "pulldown-cmark", "relative-path", "reqwest", - "route-recognizer", "rustsec", "semver", "serde", @@ -2656,6 +2649,7 @@ dependencies = [ "tokio", "toml 0.8.13", "tower", + "tower-http", "tracing", "tracing-subscriber", ] @@ -3034,6 +3028,23 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower-http" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" +dependencies = [ + "bitflags 2.5.0", + "bytes", + "http", + "http-body", + "http-body-util", + "pin-project-lite", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "tower-layer" version = "0.3.2" diff --git a/Cargo.toml b/Cargo.toml index 9eb97ba..b87e9c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,6 @@ parking_lot = "0.12" pulldown-cmark = "0.11" relative-path = { version = "1", features = ["serde"] } reqwest = { version = "0.12", features = ["json"] } -route-recognizer = "0.3" rustsec = "0.29" semver = { version = "1.0", features = ["serde"] } serde = { version = "1", features = ["derive"] } @@ -39,6 +38,7 @@ serde_urlencoded = "0.7" tokio = { version = "1.24.2", features = ["rt-multi-thread", "macros", "sync", "time"] } toml = "0.8" tower = "0.4" +tower-http = { version = "0.5", features = ["normalize-path", "trace"] } tracing = "0.1.30" tracing-subscriber = { version = "0.3", features = ["env-filter"] } diff --git a/src/main.rs b/src/main.rs index ebf76f6..4cf939a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,10 +9,9 @@ use std::{ time::Duration, }; -use axum::{extract::Request, Router}; use cadence::{QueuingMetricSink, UdpMetricSink}; use reqwest::redirect::Policy as RedirectPolicy; -use tracing::Instrument as _; +use tokio::net::TcpListener; mod engine; mod interactors; @@ -90,18 +89,8 @@ async fn main() { let app = App::new(engine.clone()); - let lst = tokio::net::TcpListener::bind(addr).await.unwrap(); - - let router = Router::new().fallback(|req: Request| async move { - let path = req.uri().path().to_owned(); - - app.handle(req) - .instrument(tracing::info_span!("@", %path)) - .await - .unwrap() - }); - - let server = axum::serve(lst, router); + let lst = TcpListener::bind(addr).await.unwrap(); + let server = axum::serve(lst, App::router().with_state(app)); tracing::info!("Server running on port {port}"); diff --git a/src/server/assets.rs b/src/server/assets.rs index 7172d7f..c0d79ee 100644 --- a/src/server/assets.rs +++ b/src/server/assets.rs @@ -9,6 +9,8 @@ pub const STATIC_STYLE_CSS_ETAG: &str = concat!( include_str!(concat!(env!("OUT_DIR"), "/style.css.sha1")), "\"" ); + +pub const STATIC_FAVICON_PATH: &str = "/static/logo.svg"; pub static STATIC_FAVICON: &[u8] = include_bytes!("../../assets/logo.svg"); pub static STATIC_LINKS_JS: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/links.js")); diff --git a/src/server/mod.rs b/src/server/mod.rs index 2bd902c..6791c4d 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,26 +1,29 @@ -use std::{env, sync::Arc, time::Instant}; +use std::env; use axum::{ body::Body, - extract::Request, + extract::{Path, Request, State}, http::{ - header::{CACHE_CONTROL, CONTENT_TYPE, ETAG, LOCATION}, - Method, StatusCode, + header::{CACHE_CONTROL, CONTENT_TYPE, ETAG}, + StatusCode, }, - response::Response, + response::{IntoResponse as _, Redirect, Response}, + routing::get, + Router, }; use badge::BadgeStyle; use futures_util::future; use once_cell::sync::Lazy; -use route_recognizer::{Params, Router}; use semver::VersionReq; use serde::Deserialize; +use tower_http::{normalize_path::NormalizePathLayer, trace::TraceLayer}; mod assets; mod views; use self::assets::{ - STATIC_LINKS_JS_ETAG, STATIC_LINKS_JS_PATH, STATIC_STYLE_CSS_ETAG, STATIC_STYLE_CSS_PATH, + STATIC_FAVICON_PATH, STATIC_LINKS_JS_ETAG, STATIC_LINKS_JS_PATH, STATIC_STYLE_CSS_ETAG, + STATIC_STYLE_CSS_PATH, }; use crate::{ engine::{AnalyzeDependenciesOutcome, Engine}, @@ -44,116 +47,54 @@ enum StaticFile { LinksJs, } -enum Route { - Index, - Static(StaticFile), - RepoStatus(StatusFormat), - CrateRedirect, - CrateStatus(StatusFormat), - LatestCrateBadge, -} - #[derive(Clone)] pub struct App { engine: Engine, - router: Arc>, } impl App { pub fn new(engine: Engine) -> App { - let mut router = Router::new(); - - router.add("/", Route::Index); - - router.add(STATIC_STYLE_CSS_PATH, Route::Static(StaticFile::StyleCss)); - router.add("/static/logo.svg", Route::Static(StaticFile::FaviconPng)); - router.add(STATIC_LINKS_JS_PATH, Route::Static(StaticFile::LinksJs)); - - router.add( - "/repo/*site/:qual/:name", - Route::RepoStatus(StatusFormat::Html), - ); - router.add( - "/repo/*site/:qual/:name/status.svg", - Route::RepoStatus(StatusFormat::Svg), - ); - - router.add("/crate/:name", Route::CrateRedirect); - router.add( - "/crate/:name/:version", - Route::CrateStatus(StatusFormat::Html), - ); - router.add("/crate/:name/latest/status.svg", Route::LatestCrateBadge); - router.add( - "/crate/:name/:version/status.svg", - Route::CrateStatus(StatusFormat::Svg), - ); - - App { - engine, - router: Arc::new(router), - } + App { engine } } - pub async fn handle(&self, req: Request) -> Result, axum::Error> { - let start = Instant::now(); - - // allows `/path/` to also match `/path` - let normalized_path = req.uri().path().trim_end_matches('/'); - - let res = if let Ok(route_match) = self.router.recognize(normalized_path) { - match (req.method(), route_match.handler()) { - (&Method::GET, Route::Index) => self.index(req, route_match.params().clone()).await, - - (&Method::GET, Route::RepoStatus(format)) => { - self.repo_status(req, route_match.params().clone(), *format) - .await - } - - (&Method::GET, Route::CrateStatus(format)) => { - 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 { - Ok(not_found()) - }; - - let end = Instant::now(); - let diff = end - start; - - match &res { - Ok(res) => tracing::info!( - status = %res.status(), - time = %format_args!("{}ms", diff.as_millis()), - ), - Err(err) => tracing::error!(%err), - }; - - res + pub(crate) fn router() -> Router { + Router::new() + .route("/", get(App::index)) + .route("/crate/:name", get(App::crate_redirect)) + .route("/crate/:name/:version", get(App::crate_status_html)) + .route( + "/crate/:name/latest/status.svg", + get(App::crate_latest_status_svg), + ) + .route( + "/crate/:name/:version/status.svg", + get(App::crate_status_svg), + ) + // TODO: `:site` isn't quite right, original was `*site` + .route("/repo/:site/:qual/:name", get(App::repo_status_html)) + .route( + "/repo/:site/:qual/:name/status.svg", + get(App::repo_status_svg), + ) + .route( + STATIC_STYLE_CSS_PATH, + get(|| App::static_file(StaticFile::StyleCss)), + ) + .route( + STATIC_FAVICON_PATH, + get(|| App::static_file(StaticFile::FaviconPng)), + ) + .route( + STATIC_LINKS_JS_PATH, + get(|| App::static_file(StaticFile::LinksJs)), + ) + .fallback(|| async { not_found() }) + .layer(NormalizePathLayer::trim_trailing_slash()) + .layer(TraceLayer::new_for_http()) } -} -impl App { - async fn index( - &self, - _req: Request, - _params: Params, - ) -> Result, axum::Error> { - let engine = self.engine.clone(); + async fn index(State(app): State) -> Response { + let engine = app.engine.clone(); let popular = future::try_join(engine.get_popular_repos(), engine.get_popular_crates()).await; @@ -164,29 +105,42 @@ impl App { let mut response = views::html::error::render("Could not retrieve popular items", ""); *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; - Ok(response) + response } + Ok((popular_repos, popular_crates)) => { - Ok(views::html::index::render(popular_repos, popular_crates)) + views::html::index::render(popular_repos, popular_crates) } } } + async fn repo_status_html( + State(app): State, + Path(params): Path<(String, String, String)>, + req: Request, + ) -> Response { + Self::repo_status(app, params, req, StatusFormat::Html).await + } + + async fn repo_status_svg( + State(app): State, + Path(params): Path<(String, String, String)>, + req: Request, + ) -> Response { + Self::repo_status(app, params, req, StatusFormat::Svg).await + } + async fn repo_status( - &self, - req: Request, - params: Params, + app: App, + (site, qual, name): (String, String, String), + req: Request, format: StatusFormat, - ) -> Result, axum::Error> { - let server = self.clone(); - - let site = params.find("site").expect("route param 'site' not found"); - let qual = params.find("qual").expect("route param 'qual' not found"); - let name = params.find("name").expect("route param 'name' not found"); + ) -> Response { + let engine = app.engine.clone(); let extra_knobs = ExtraConfig::from_query_string(req.uri().query()); - let repo_path_result = RepoPath::from_parts(site, qual, name); + let repo_path_result = RepoPath::from_parts(&site, &qual, &name); match repo_path_result { Err(err) => { @@ -196,48 +150,40 @@ impl App { "Please make sure to provide a valid repository path.", ); *response.status_mut() = StatusCode::BAD_REQUEST; - Ok(response) + response } Ok(repo_path) => { - let analyze_result = server - .engine + let analyze_result = engine .analyze_repo_dependencies(repo_path.clone(), &extra_knobs.path) .await; match analyze_result { Err(err) => { tracing::error!(%err); - let response = App::status_format_analysis( + + App::status_format_analysis( None, format, SubjectPath::Repo(repo_path), extra_knobs, - ); - Ok(response) - } - Ok(analysis_outcome) => { - let response = App::status_format_analysis( - Some(analysis_outcome), - format, - SubjectPath::Repo(repo_path), - extra_knobs, - ); - Ok(response) + ) } + + Ok(analysis_outcome) => App::status_format_analysis( + Some(analysis_outcome), + format, + SubjectPath::Repo(repo_path), + extra_knobs, + ), } } } } - async fn crate_redirect( - &self, - _req: Request, - params: Params, - ) -> Result, axum::Error> { - let engine = self.engine.clone(); + async fn crate_redirect(State(app): State, Path((name,)): Path<(String,)>) -> Response { + let engine = app.engine.clone(); - let name = params.find("name").expect("route param 'name' not found"); let crate_name_result = name.parse::(); match crate_name_result { @@ -248,7 +194,7 @@ impl App { "Please make sure to provide a valid crate name.", ); *response.status_mut() = StatusCode::BAD_REQUEST; - Ok(response) + response } Ok(crate_name) => { @@ -264,7 +210,7 @@ impl App { "Please make sure to provide a valid crate name.", ); *response.status_mut() = StatusCode::NOT_FOUND; - Ok(response) + response } Ok(None) => { let mut response = views::html::error::render( @@ -272,7 +218,7 @@ impl App { "Please make sure to provide a valid crate name.", ); *response.status_mut() = StatusCode::NOT_FOUND; - Ok(response) + response } Ok(Some(release)) => { let redirect_url = format!( @@ -282,31 +228,48 @@ impl App { release.version ); - let res = Response::builder() - .status(StatusCode::TEMPORARY_REDIRECT) - .header(LOCATION, redirect_url) - .body(Body::empty()) - .unwrap(); - - Ok(res) + Redirect::temporary(&redirect_url).into_response() } } } } } + async fn crate_status_html( + State(app): State, + Path((name, version)): Path<(String, String)>, + req: Request, + ) -> Response { + Self::crate_status(app, (name, Some(version)), req, StatusFormat::Html).await + } + + async fn crate_status_svg( + State(app): State, + Path((name, version)): Path<(String, String)>, + req: Request, + ) -> Response { + Self::crate_status(app, (name, Some(version)), req, StatusFormat::Svg).await + } + + async fn crate_latest_status_svg( + State(app): State, + Path((name,)): Path<(String,)>, + req: Request, + ) -> Response { + Self::crate_status(app, (name, None), req, StatusFormat::Svg).await + } + async fn crate_status( - &self, - req: Request, - params: Params, + app: App, + (name, version): (String, Option), + req: Request, format: StatusFormat, - ) -> Result, axum::Error> { - let server = self.clone(); - - let name = params.find("name").expect("route param 'name' not found"); + ) -> Response { + let server = app.clone(); - let version = match params.find("version") { + let version = match version { Some(ver) => ver.to_owned(), + None => { let crate_name = match name.parse() { Ok(name) => name, @@ -316,7 +279,7 @@ impl App { "Please make sure to provide a valid crate name and version.", ); *response.status_mut() = StatusCode::BAD_REQUEST; - return Ok(response); + return response; } }; @@ -326,7 +289,7 @@ impl App { .await { Ok(Some(latest_rel)) => latest_rel.version.to_string(), - Ok(None) => return Ok(not_found()), + Ok(None) => return not_found(), Err(err) => { tracing::error!(%err); let mut response = views::html::error::render( @@ -334,13 +297,13 @@ impl App { "Please make sure to provide a valid crate name.", ); *response.status_mut() = StatusCode::NOT_FOUND; - return Ok(response); + return response; } } } }; - let crate_path_result = CratePath::from_parts(name, &version); + let crate_path_result = CratePath::from_parts(&name, &version); let badge_knobs = ExtraConfig::from_query_string(req.uri().query()); match crate_path_result { @@ -351,8 +314,9 @@ impl App { "Please make sure to provide a valid crate name and version.", ); *response.status_mut() = StatusCode::BAD_REQUEST; - Ok(response) + response } + Ok(crate_path) => { let analyze_result = server .engine @@ -362,24 +326,20 @@ impl App { match analyze_result { Err(err) => { tracing::error!(%err); - let response = App::status_format_analysis( + App::status_format_analysis( None, format, SubjectPath::Crate(crate_path), badge_knobs, - ); - Ok(response) + ) } - Ok(analysis_outcome) => { - let response = App::status_format_analysis( - Some(analysis_outcome), - format, - SubjectPath::Crate(crate_path), - badge_knobs, - ); - Ok(response) - } + Ok(analysis_outcome) => App::status_format_analysis( + Some(analysis_outcome), + format, + SubjectPath::Crate(crate_path), + badge_knobs, + ), } } } @@ -390,7 +350,7 @@ impl App { format: StatusFormat, subject_path: SubjectPath, badge_knobs: ExtraConfig, - ) -> Response { + ) -> Response { match format { StatusFormat::Svg => views::badge::response(analysis_outcome.as_ref(), badge_knobs), StatusFormat::Html => { @@ -399,7 +359,7 @@ impl App { } } - fn static_file(file: StaticFile) -> Response { + async fn static_file(file: StaticFile) -> Response { match file { StaticFile::StyleCss => Response::builder() .header(CONTENT_TYPE, "text/css; charset=utf-8") @@ -407,10 +367,12 @@ impl App { .header(CACHE_CONTROL, "public, max-age=365000000, immutable") .body(Body::from(assets::STATIC_STYLE_CSS)) .unwrap(), + StaticFile::FaviconPng => Response::builder() .header(CONTENT_TYPE, "image/svg+xml") .body(Body::from(assets::STATIC_FAVICON)) .unwrap(), + StaticFile::LinksJs => Response::builder() .header(CONTENT_TYPE, "text/javascript; charset=utf-8") .header(ETAG, STATIC_LINKS_JS_ETAG) @@ -421,7 +383,7 @@ impl App { } } -fn not_found() -> Response { +fn not_found() -> Response { views::html::error::render_404() }