From ab9bb894577b514d857792e683d231730a7da4d2 Mon Sep 17 00:00:00 2001 From: Jacobus Geluk Date: Sat, 22 Aug 2026 17:58:17 +0100 Subject: [PATCH 1/2] refactor(fs): use async build asset operations --- Cargo.lock | 180 +++++++++++++++++++++++++-- crates/openapi-route-axum/Cargo.toml | 2 + crates/openapi-route-axum/build.rs | 57 ++++++--- 3 files changed, 211 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 790a428..f364410 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,19 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "async-fs-io" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "075ef53066a95849d90d4899af6a0c9000ab10396120d39c9756c5d0b0fad69d" +dependencies = [ + "fs2", + "rhusky", + "thiserror", + "tokio", + "uuid", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -87,6 +100,12 @@ dependencies = [ "hybrid-array", ] +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + [[package]] name = "bytes" version = "1.12.1" @@ -95,9 +114,9 @@ checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" [[package]] name = "cc" -version = "1.4.2" +version = "1.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e" +checksum = "0ad534f4357a5264cce5019c989cf66a4f0dc4e0d1b1d15f8aacec0ff7360273" dependencies = [ "find-msvc-tools", "shlex", @@ -126,9 +145,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +checksum = "8498c871161e1742aaa9d52551b2d6ebdd4c3d45a3be423e3728f33b955be550" dependencies = [ "cfg-if", ] @@ -181,9 +200,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de" +checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890" [[package]] name = "flate2" @@ -204,6 +223,16 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "futures-channel" version = "0.3.34" @@ -248,6 +277,17 @@ dependencies = [ "wasi", ] +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "libc", + "r-efi", +] + [[package]] name = "hashbrown" version = "0.17.1" @@ -370,6 +410,17 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +[[package]] +name = "js-sys" +version = "0.3.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + [[package]] name = "libc" version = "0.2.189" @@ -384,9 +435,9 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "log" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" [[package]] name = "matchit" @@ -448,11 +499,13 @@ dependencies = [ name = "openapi-route-axum" version = "0.1.2" dependencies = [ + "async-fs-io", "axum", "flate2", "openapi-route", "sha2", "tar", + "tokio", "ureq", ] @@ -495,6 +548,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + [[package]] name = "rhusky" version = "0.0.6" @@ -509,7 +568,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom", + "getrandom 0.2.17", "libc", "untrusted", "windows-sys 0.52.0", @@ -554,9 +613,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.14" +version = "0.103.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0527518605e68109d875e248ea259b6758801cf165e4b2c2733ae3b51f12535a" +checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" dependencies = [ "ring", "rustls-pki-types", @@ -731,12 +790,33 @@ dependencies = [ "xattr", ] +[[package]] +name = "thiserror" +version = "2.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + [[package]] name = "tokio" version = "1.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" dependencies = [ + "bytes", "libc", "mio", "pin-project-lite", @@ -880,12 +960,68 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "uuid" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f053576934f05a761a402421fbbe3d425d9366f75f978806a037b3ca481abecc" +dependencies = [ + "getrandom 0.4.3", + "js-sys", + "wasm-bindgen", +] + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" +[[package]] +name = "wasm-bindgen" +version = "0.2.127" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.127" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.127" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.119", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.127" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf" +dependencies = [ + "unicode-ident", +] + [[package]] name = "webpki-roots" version = "1.0.9" @@ -895,6 +1031,28 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-link" version = "0.2.1" diff --git a/crates/openapi-route-axum/Cargo.toml b/crates/openapi-route-axum/Cargo.toml index a9f15bc..5c67c5c 100644 --- a/crates/openapi-route-axum/Cargo.toml +++ b/crates/openapi-route-axum/Cargo.toml @@ -15,7 +15,9 @@ axum.workspace = true openapi-route.workspace = true [build-dependencies] +async-fs-io = "0.1.5" ureq.workspace = true flate2.workspace = true tar.workspace = true sha2.workspace = true +tokio = { version = "1", features = ["fs", "io-util", "macros", "rt-multi-thread"] } diff --git a/crates/openapi-route-axum/build.rs b/crates/openapi-route-axum/build.rs index cb4ada2..b58f277 100644 --- a/crates/openapi-route-axum/build.rs +++ b/crates/openapi-route-axum/build.rs @@ -15,6 +15,8 @@ use std::io::Read; use std::path::{Path, PathBuf}; +use async_fs_io::{ensure_dir, metadata, read_bounded, try_exists, write_bytes}; + /// Pinned Swagger UI version. Upgrading is a deliberate change to /// both constants, reviewed like any dependency bump. const SWAGGER_UI_VERSION: &str = "5.32.12"; @@ -33,7 +35,8 @@ const ASSET_FILES: &[&str] = &[ "favicon-16x16.png", ]; -fn main() { +#[tokio::main] +async fn main() { println!("cargo::rerun-if-changed=build.rs"); println!("cargo::rerun-if-env-changed=OPENAPI_ROUTE_SWAGGER_UI_TARBALL"); println!("cargo::rustc-env=OPENAPI_ROUTE_SWAGGER_UI_VERSION={SWAGGER_UI_VERSION}"); @@ -41,33 +44,50 @@ fn main() { let out_dir = PathBuf::from(std::env::var("OUT_DIR").expect("cargo sets OUT_DIR")); let asset_dir = out_dir.join("swagger-ui"); - if assets_present(&asset_dir) { + if assets_present(&asset_dir).await { return; } - let tarball = obtain_tarball(); + let tarball = obtain_tarball().await; verify_sha256(&tarball); - extract_assets(&tarball, &asset_dir); + extract_assets(&tarball, &asset_dir).await; } /// All expected files already extracted (a previous build of the same /// pinned version — `OUT_DIR` is per-version by way of the checksum /// gate below, so stale assets cannot survive an upgrade unnoticed). -fn assets_present(asset_dir: &Path) -> bool { +async fn assets_present(asset_dir: &Path) -> bool { let marker = asset_dir.join(format!(".version-{SWAGGER_UI_VERSION}")); - marker.is_file() - && ASSET_FILES - .iter() - .all(|name| asset_dir.join(name).is_file()) + if !is_file(&marker).await { + return false; + } + for name in ASSET_FILES { + if !is_file(&asset_dir.join(name)).await { + return false; + } + } + true +} + +async fn is_file(path: &Path) -> bool { + if !try_exists(path).await.expect("inspect generated asset path") { + return false; + } + !metadata(path) + .await + .expect("inspect generated asset metadata") + .is_directory } /// The tarball bytes: a local override for offline builds, else the /// npm registry. -fn obtain_tarball() -> Vec { +async fn obtain_tarball() -> Vec { if let Ok(local) = std::env::var("OPENAPI_ROUTE_SWAGGER_UI_TARBALL") { - return std::fs::read(&local).unwrap_or_else(|e| { - panic!("OPENAPI_ROUTE_SWAGGER_UI_TARBALL={local} is not readable: {e}") - }); + return read_bounded(&local, 256 * 1024 * 1024) + .await + .unwrap_or_else(|e| { + panic!("OPENAPI_ROUTE_SWAGGER_UI_TARBALL={local} is not readable: {e}") + }); } let url = format!( @@ -101,8 +121,9 @@ fn verify_sha256(tarball: &[u8]) { } /// Extract exactly [`ASSET_FILES`] from `package/` into `asset_dir`. -fn extract_assets(tarball: &[u8], asset_dir: &Path) { - std::fs::create_dir_all(asset_dir) +async fn extract_assets(tarball: &[u8], asset_dir: &Path) { + ensure_dir(asset_dir) + .await .unwrap_or_else(|e| panic!("creating {}: {e}", asset_dir.display())); let mut archive = tar::Archive::new(flate2::read::GzDecoder::new(tarball)); @@ -122,7 +143,8 @@ fn extract_assets(tarball: &[u8], asset_dir: &Path) { entry .read_to_end(&mut contents) .unwrap_or_else(|e| panic!("reading {name} from tarball: {e}")); - std::fs::write(&target, contents) + write_bytes(&target, &contents) + .await .unwrap_or_else(|e| panic!("writing {}: {e}", target.display())); remaining.swap_remove(index); } @@ -132,6 +154,7 @@ fn extract_assets(tarball: &[u8], asset_dir: &Path) { ); let marker = asset_dir.join(format!(".version-{SWAGGER_UI_VERSION}")); - std::fs::write(&marker, TARBALL_SHA256) + write_bytes(&marker, TARBALL_SHA256.as_bytes()) + .await .unwrap_or_else(|e| panic!("writing {}: {e}", marker.display())); } From 40c63b63530a45a3267f18e73fa0e04a8bfade8f Mon Sep 17 00:00:00 2001 From: Jacobus Geluk Date: Sun, 23 Aug 2026 01:21:48 +0100 Subject: [PATCH 2/2] style(format): apply repository rustfmt --- crates/openapi-route-axum/build.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/openapi-route-axum/build.rs b/crates/openapi-route-axum/build.rs index b58f277..0e530f9 100644 --- a/crates/openapi-route-axum/build.rs +++ b/crates/openapi-route-axum/build.rs @@ -70,7 +70,10 @@ async fn assets_present(asset_dir: &Path) -> bool { } async fn is_file(path: &Path) -> bool { - if !try_exists(path).await.expect("inspect generated asset path") { + if !try_exists(path) + .await + .expect("inspect generated asset path") + { return false; } !metadata(path)