From f6fac25c6f55df32ff1998f9ac7c189d6fd7e897 Mon Sep 17 00:00:00 2001 From: Noratrieb <48135649+Noratrieb@users.noreply.github.com> Date: Fri, 4 Jul 2025 22:06:40 +0200 Subject: [PATCH] Remove old link and show number of builds In the users locale of course. Without any annoying Accept-Language parsing or anything like that, just with the beauty of JavaScript. --- src/db.rs | 39 ++++++++++++++++++++++++++ src/web.rs | 67 ++++++++++++++++++++++++++------------------ static/index.js | 4 +++ templates/index.html | 19 +++++++------ 4 files changed, 94 insertions(+), 35 deletions(-) create mode 100644 static/index.js diff --git a/src/db.rs b/src/db.rs index 73aa95f..ad7990c 100644 --- a/src/db.rs +++ b/src/db.rs @@ -82,6 +82,17 @@ pub struct FinishedNightlyWithBroken { pub broken_error: Option, } +pub struct BuildStats { + pub pass_count: u32, + pub error_count: u32, +} + +impl BuildStats { + pub fn total(&self) -> u32 { + self.pass_count + self.error_count + } +} + impl Db { pub async fn open(path: &str) -> Result { let db_opts = SqliteConnectOptions::from_str(path) @@ -168,6 +179,34 @@ impl Db { .map(|elems| elems.into_iter().map(|elem| elem.nightly).collect()) } + pub async fn build_count(&self) -> Result { + #[derive(sqlx::FromRow)] + struct BuildStat { + build_count: u32, + status: Status, + } + + let results = sqlx::query_as::<_, BuildStat>( + "SELECT COUNT(status) as build_count, status FROM build_info GROUP BY status", + ) + .fetch_all(&self.conn) + .await + .wrap_err("getting list of all targets")?; + + let count = |status| { + results + .iter() + .find(|row| row.status == status) + .map(|row| row.build_count) + .unwrap_or(0) + }; + + Ok(BuildStats { + pass_count: count(Status::Pass), + error_count: count(Status::Error), + }) + } + pub async fn build_status_full( &self, nightly: &str, diff --git a/src/web.rs b/src/web.rs index 0866c46..e32c047 100644 --- a/src/web.rs +++ b/src/web.rs @@ -11,7 +11,7 @@ use color_eyre::{eyre::Context, Result}; use serde::{Deserialize, Serialize}; use tracing::{error, info}; -use crate::db::{BuildInfo, BuildMode, Db, Status}; +use crate::db::{BuildInfo, BuildMode, BuildStats, Db, Status}; #[derive(Clone)] pub struct AppState { @@ -25,6 +25,7 @@ pub async fn webserver(db: Db) -> Result<()> { .route("/target", get(web_target)) .route("/nightly", get(web_nightly)) .route("/index.css", get(index_css)) + .route("/index.js", get(index_js)) .with_state(AppState { db }); info!("Serving website on port 3000 (commit {})", crate::VERSION); @@ -257,35 +258,37 @@ async fn web_nightly(State(state): State, Query(query): Query) -> impl IntoResponse { use askama::Template; - #[derive(askama::Template)] - #[template(path = "index.html")] - struct RootPage { - targets: Vec, - nightlies: Vec, - version: &'static str, - } - match state.db.target_list().await { - Ok(targets) => match state.db.nightly_list().await { - Ok(nightlies) => { - let page = RootPage { - targets, - nightlies, - version: crate::VERSION, - }; - - Html(page.render().unwrap()).into_response() - } - Err(err) => { - error!(?err, "Error loading nightly state"); - StatusCode::INTERNAL_SERVER_ERROR.into_response() - } - }, - Err(err) => { - error!(?err, "Error loading target state"); - StatusCode::INTERNAL_SERVER_ERROR.into_response() + async fn render(state: AppState) -> Result { + #[derive(askama::Template)] + #[template(path = "index.html")] + struct RootPage { + targets: Vec, + nightlies: Vec, + version: &'static str, + build_count: BuildStats, } + + let targets = state.db.target_list().await?; + let nightlies = state.db.nightly_list().await?; + let build_count = state.db.build_count().await?; + + let page = RootPage { + targets, + nightlies, + version: crate::VERSION, + build_count, + }; + + Ok(Html(page.render().unwrap()).into_response()) } + + render(state) + .await + .unwrap_or_else(|err: color_eyre::eyre::Error| { + error!(?err, "Error loading data for root page"); + StatusCode::INTERNAL_SERVER_ERROR.into_response() + }) } async fn index_css() -> impl IntoResponse { @@ -298,6 +301,16 @@ async fn index_css() -> impl IntoResponse { ) } +async fn index_js() -> impl IntoResponse { + ( + [( + axum::http::header::CONTENT_TYPE, + axum::http::HeaderValue::from_static("application/javascript; charset=utf-8"), + )], + include_str!("../static/index.js"), + ) +} + #[derive(Serialize, Deserialize)] struct TriggerBuildBody { nightly: String, diff --git a/static/index.js b/static/index.js new file mode 100644 index 0000000..37a6647 --- /dev/null +++ b/static/index.js @@ -0,0 +1,4 @@ +// Do some fancy number formatting in your locale, so you can't blame me if the numbers look ass. +document.querySelectorAll(".number").forEach((elem) => { + elem.textContent = new Intl.NumberFormat().format(Number(elem.textContent)); +}); diff --git a/templates/index.html b/templates/index.html index 421a894..b6a270c 100644 --- a/templates/index.html +++ b/templates/index.html @@ -5,21 +5,24 @@ Does it build? - +

Does it build?

- This website builds every rustc target on many nightlies to check which - ones work and which ones do not. + This website builds the standard libary for every rustc target on every + nightly to check whether it builds or not. +

+

+ There are currently + {{build_count.total()}} build results stored, + with {{build_count.pass_count}} passing and + {{build_count.error_count}} erroring.

-

You can select a target from the list below and check its history.

- If that is not what you want, you can visit the - full table to see - all information at once (which will really stress test - your browser). + You can select a nightly or target from the list below and check its + history.

Nightlies