From bf11fd7fd9abba16c0faaf10b2e9636760f7c4d6 Mon Sep 17 00:00:00 2001 From: Simon Klee Date: Sat, 7 Nov 2026 03:48:51 +0100 Subject: [PATCH] perf(cli-rs): build url without ICU by pinning idna_adapter 1.0.0 The idna_adapter 1.0.0 stub turns IDNA off in url, which drops ICU4X: 21 crates, 2 build scripts and 4 proc macros. A non-ASCII host now fails to parse; punycode hosts still work. The client names the cause when a base URL such as --server has one (deviation A10-ascii-hosts, decision E1). --- packages/cli-rs/Cargo.lock | 228 +----------------- packages/cli-rs/Cargo.toml | 3 + packages/cli-rs/crates/api/Cargo.toml | 1 + packages/cli-rs/crates/api/src/client.rs | 9 +- packages/cli-rs/crates/api/tests/api.rs | 16 ++ .../parity/deviations/A10-ascii-hosts.md | 21 ++ 6 files changed, 51 insertions(+), 227 deletions(-) create mode 100644 packages/cli-rs/parity/deviations/A10-ascii-hosts.md diff --git a/packages/cli-rs/Cargo.lock b/packages/cli-rs/Cargo.lock index 8edd17976b1..5979eba155c 100644 --- a/packages/cli-rs/Cargo.lock +++ b/packages/cli-rs/Cargo.lock @@ -292,17 +292,6 @@ dependencies = [ "crypto-common", ] -[[package]] -name = "displaydoc" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.9", -] - [[package]] name = "equivalent" version = "1.0.3" @@ -577,89 +566,6 @@ dependencies = [ "cc", ] -[[package]] -name = "icu_collections" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe090a42a0d3140fed8c63263ed1fecc02422cb2f4bfe55cd8de2de1b8e105dd" -dependencies = [ - "displaydoc", - "potential_utf", - "utf8_iter", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locale_core" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25d29b57c17c6528d0dc4c2141cfa2d08200465acb537a3909c8b9b8a4a984c0" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "404a3b8c486f205c87fe7f23ab6894d84a4ad76d6e4671ae1981ba7def87c599" -dependencies = [ - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20f0088bfb7441f7a37fa0c9e20cb1e9cba1cac6b9ce8cd2a6dc44294f8e8cbc" - -[[package]] -name = "icu_properties" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ea83d991c56158f7cf898bf86436c1dfe7c07ee82fe88951f2695c9f37a379f" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "zerotrie", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98f29af82d02b163fe50161388350e743bb3107cc8e8c2f7314cb505faddfcf" - -[[package]] -name = "icu_provider" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413099315636c588ee8f011190b329b971a09a8fae9d157e01dcae8379468514" -dependencies = [ - "displaydoc", - "icu_locale_core", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", -] - [[package]] name = "idna" version = "1.1.0" @@ -673,13 +579,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.2" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" -dependencies = [ - "icu_normalizer", - "icu_properties", -] +checksum = "cfdf4f5d937a025381f5ab13624b1c5f51414bfe5c9885663226eae8d6d39560" [[package]] name = "indexmap" @@ -833,12 +735,6 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" -[[package]] -name = "litemap" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" - [[package]] name = "log" version = "0.4.35" @@ -961,6 +857,7 @@ dependencies = [ "base64", "bytes", "futures-util", + "idna_adapter", "indexmap", "log", "memchr", @@ -1302,15 +1199,6 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" -[[package]] -name = "potential_utf" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661" -dependencies = [ - "zerovec", -] - [[package]] name = "proc-macro2" version = "1.0.107" @@ -1712,12 +1600,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "stable_deref_trait" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" - [[package]] name = "streaming-iterator" version = "0.1.9" @@ -1767,17 +1649,6 @@ dependencies = [ "futures-core", ] -[[package]] -name = "synstructure" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "901704edd0dfe137f1987838ee4f259e4e063c31371bdb423f7ae38ec6f77f02" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.9", -] - [[package]] name = "tempfile" version = "3.27.0" @@ -1821,16 +1692,6 @@ dependencies = [ "syn 3.0.9", ] -[[package]] -name = "tinystr" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abc840c238154d58f9e86b2792261edfde565a17696e1cda1a449de27979801a" -dependencies = [ - "displaydoc", - "zerovec", -] - [[package]] name = "tokio" version = "1.54.1" @@ -2688,95 +2549,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "writeable" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" - -[[package]] -name = "yoke" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "417ef3fcf6c300b7dbd25c85531a746931c02ad536f3cf4786ed1e88223340b3" -dependencies = [ - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec8ebde2db3681e8c9980cc27822030e68752690ddfa9473e739aeb4dbde6d71" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.9", - "synstructure", -] - -[[package]] -name = "zerofrom" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f75b4683f6c7f45248d4d64056a24298c6281e0993356d7d1b4a1a962ef10d4a" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.9", - "synstructure", -] - [[package]] name = "zeroize" version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e13084392c5e4bc371903e2935a5eaeed24905a7511356b883835e18a78f6879" -[[package]] -name = "zerotrie" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f58a2799cfe9a1670c7ea8edbd52ef8edb93cb37a7f83bf31ba9e35d9dae6a40" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.11.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3743ddbf47738d929cd1ae2e199d40d0162979dfe31aef8ae646d2bf00bf97a" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.9", -] - [[package]] name = "zmij" version = "1.0.23" diff --git a/packages/cli-rs/Cargo.toml b/packages/cli-rs/Cargo.toml index ebf326e766b..256188c9b10 100644 --- a/packages/cli-rs/Cargo.toml +++ b/packages/cli-rs/Cargo.toml @@ -40,6 +40,9 @@ chrono = { version = "0.4", default-features = false, features = ["std", "clock" clap = { version = "4", features = ["derive", "wrap_help"] } clap_complete = "4" futures-util = { version = "0.3", default-features = false, features = ["std", "sink"] } +# The IDNA stub: `url` rejects non-ASCII hosts instead of pulling ICU4X (deviation A10-ascii-hosts). Pinned so a +# `cargo update` cannot bring 1.2 back. +idna_adapter = "=1.0.0" indexmap = { version = "2", features = ["serde"] } libloading = "0.9" log = { version = "0.4", features = ["std"] } diff --git a/packages/cli-rs/crates/api/Cargo.toml b/packages/cli-rs/crates/api/Cargo.toml index 3b62cdc5a50..12a2cea5ae9 100644 --- a/packages/cli-rs/crates/api/Cargo.toml +++ b/packages/cli-rs/crates/api/Cargo.toml @@ -14,6 +14,7 @@ opencode-api-types.workspace = true base64.workspace = true bytes.workspace = true futures-util.workspace = true +idna_adapter.workspace = true log.workspace = true memchr.workspace = true percent-encoding.workspace = true diff --git a/packages/cli-rs/crates/api/src/client.rs b/packages/cli-rs/crates/api/src/client.rs index 0f2dd49b481..81e760a3989 100644 --- a/packages/cli-rs/crates/api/src/client.rs +++ b/packages/cli-rs/crates/api/src/client.rs @@ -77,8 +77,13 @@ impl Client { } pub fn with_transport(base_url: &str, transport: Arc) -> Result { - let mut base_url = - url::Url::parse(base_url).map_err(|error| Error::Transport(format!("{base_url}: {error}")))?; + let mut base_url = url::Url::parse(base_url).map_err(|error| match error { + // `url` is built without IDNA (deviation A10-ascii-hosts). + url::ParseError::IdnaError => Error::Transport(format!( + "{base_url}: a host name must be ASCII; write an international name in its punycode (xn--) form" + )), + error => Error::Transport(format!("{base_url}: {error}")), + })?; // A leading slash would replace any path prefix on the base URL, so paths join relative to it. if !base_url.path().ends_with('/') { let path = format!("{}/", base_url.path()); diff --git a/packages/cli-rs/crates/api/tests/api.rs b/packages/cli-rs/crates/api/tests/api.rs index 03672360374..a8b73ea3f90 100644 --- a/packages/cli-rs/crates/api/tests/api.rs +++ b/packages/cli-rs/crates/api/tests/api.rs @@ -44,6 +44,22 @@ fn client(script: &Arc