diff --git a/.github/workflows/_ci-rust.yml b/.github/workflows/_ci-rust.yml index e25639d3ac..4f615eb162 100644 --- a/.github/workflows/_ci-rust.yml +++ b/.github/workflows/_ci-rust.yml @@ -120,7 +120,7 @@ jobs: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 # MSVC needs windows.h (aws-lc-sys et al.), so this runs on a real Windows # runner — hermit, used by the Linux jobs, does not provide MSVC. The - # toolchain (1.95.0 + clippy via profile = default) comes from the + # toolchain (1.98.1 + clippy via profile = default) comes from the # repo-root rust-toolchain.toml, which the runner's preinstalled rustup # honors on demand; the host triple already is x86_64-pc-windows-msvc. - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 040817361a..26fbbf5401 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -676,7 +676,7 @@ jobs: shell: bash run: scripts/verify-release-ref.sh desktop-v "$VERSION" - - uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0 + - uses: dtolnay/rust-toolchain@18f0487477426dce244608110aa2660c2893deb7 # 1.98.1 with: targets: ${{ env.TARGET }} diff --git a/.github/workflows/windows-canary.yml b/.github/workflows/windows-canary.yml index 7093efd2dc..03114c1a47 100644 --- a/.github/workflows/windows-canary.yml +++ b/.github/workflows/windows-canary.yml @@ -39,7 +39,7 @@ jobs: persist-credentials: false # The Windows runner ships with rustup preinstalled; rust-toolchain.toml - # in the repo root pins the channel (1.95.0) automatically. We only need + # in the repo root pins the channel (1.98.1) automatically. We only need # to ensure the cross-compile target is registered; on windows-latest the # host IS x86_64-pc-windows-msvc so this is typically a no-op. - name: Add Rust target diff --git a/Dockerfile b/Dockerfile index 1f10b0f260..02446e144f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ # # Public Buzz relay image — published as ghcr.io/block/buzz:. # -# Builds the `buzz-relay` binary (Rust 1.95) and the `buzz-web` static bundle +# Builds the `buzz-relay` binary (Rust 1.98) and the `buzz-web` static bundle # (pnpm + vite), then assembles them into a small debian-slim runtime with # `git` available (the relay shells out to git for repo hydrate / receive-pack # / upload-pack — see crates/buzz-relay/src/api/git). @@ -11,7 +11,7 @@ # native arm64 runners (see .github/workflows/docker.yml). The Dockerfile # itself is platform-agnostic; do not add --platform pins. -ARG RUST_VERSION=1.95 +ARG RUST_VERSION=1.98 ARG NODE_VERSION=24 ARG DEBIAN_VERSION=bookworm diff --git a/Dockerfile.push-gateway b/Dockerfile.push-gateway index 2b899c89e5..f91e21b58d 100644 --- a/Dockerfile.push-gateway +++ b/Dockerfile.push-gateway @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:1.7 -ARG RUST_VERSION=1.95 +ARG RUST_VERSION=1.98 ARG DEBIAN_VERSION=bookworm FROM rust:${RUST_VERSION}-${DEBIAN_VERSION} AS chef diff --git a/Dockerfile.sprig b/Dockerfile.sprig index ee8ac1a64f..a2867f53d0 100644 --- a/Dockerfile.sprig +++ b/Dockerfile.sprig @@ -2,7 +2,7 @@ # Multi-arch is produced by building this file on native amd64 and arm64 runners. # Keep both bases pinned to manifest-list digests so either architecture resolves # to immutable source bytes. -FROM rust:1.95-alpine3.22@sha256:064dfc925d68d1a63f4fd2871bd7dc6e6ea56692989a487185855d62885d90aa AS builder +FROM rust:1.98-alpine3.22@sha256:a1796ca6fa216d6727b5f61c69e4c665b120b1a4dcb969639e2f25f1ed309456 AS builder RUN apk add --no-cache \ build-base \ diff --git a/benchmarks/harbor-buzz-orchestra/scripts/benchmark.py b/benchmarks/harbor-buzz-orchestra/scripts/benchmark.py index 0ad028796c..211730a8d2 100755 --- a/benchmarks/harbor-buzz-orchestra/scripts/benchmark.py +++ b/benchmarks/harbor-buzz-orchestra/scripts/benchmark.py @@ -75,7 +75,7 @@ AGENT_BINARIES = ("buzz-acp", "buzz-agent", "buzz-dev-mcp") FORWARDER_SOURCE = PACKAGE_ROOT / "forwarder" / "relay_forwarder.rs" FORWARDER_BINARY = "relay-forwarder" LINUX_TARGET_DIR = STATE_DIR / "linux-target" -RUST_IMAGE = "rust:1.95-alpine" +RUST_IMAGE = "rust:1.98-alpine" @dataclass(frozen=True) diff --git a/crates/buzz-acp/src/queue.rs b/crates/buzz-acp/src/queue.rs index cc5ae876c1..1cf53baef9 100644 --- a/crates/buzz-acp/src/queue.rs +++ b/crates/buzz-acp/src/queue.rs @@ -558,28 +558,24 @@ impl EventQueue { .keys() .find(|scope| !self.in_flight_scopes.contains(scope)) .cloned(); - match cancelled_scope { - Some(scope) => { - // Move cancelled events into the regular events slot. - // No new events to merge — re-dispatch the original batch. - let cancelled = self.cancelled_batches.remove(&scope).unwrap_or_default(); - let cancel_reason = self.cancel_reasons.remove(&scope); - self.in_flight_scopes.insert(scope.clone()); - self.in_flight_deadlines - .insert(scope.clone(), now + self.in_flight_deadline); - self.in_flight_batch_sizes - .insert(scope.clone(), cancelled.len()); - self.record_in_flight_reply_route(&scope, &cancelled); - return Some(FlushBatch { - channel_id: scope.channel_id(), - scope, - events: cancelled, - cancelled_events: vec![], - cancel_reason, - }); - } - None => return None, - } + let scope = cancelled_scope?; + // Move cancelled events into the regular events slot. + // No new events to merge — re-dispatch the original batch. + let cancelled = self.cancelled_batches.remove(&scope).unwrap_or_default(); + let cancel_reason = self.cancel_reasons.remove(&scope); + self.in_flight_scopes.insert(scope.clone()); + self.in_flight_deadlines + .insert(scope.clone(), now + self.in_flight_deadline); + self.in_flight_batch_sizes + .insert(scope.clone(), cancelled.len()); + self.record_in_flight_reply_route(&scope, &cancelled); + return Some(FlushBatch { + channel_id: scope.channel_id(), + scope, + events: cancelled, + cancelled_events: vec![], + cancel_reason, + }); } }; let channel_id = scope.channel_id(); @@ -1465,10 +1461,8 @@ pub fn extract_slash_command(content: &str, known_names: &[&str]) -> Option 0).then_some(len) }); - match name_len { - Some(len) => rest = after_at[len..].trim_start(), - None => return None, // bare '@' — not a mention - } + let len = name_len?; // bare '@' — not a mention + rest = after_at[len..].trim_start(); } else { break; } diff --git a/crates/buzz-agent/src/hints.rs b/crates/buzz-agent/src/hints.rs index 9fb99f0ebc..22c4b4fcac 100644 --- a/crates/buzz-agent/src/hints.rs +++ b/crates/buzz-agent/src/hints.rs @@ -30,10 +30,7 @@ fn find_git_root(start: &Path) -> Option { if current.join(".git").exists() { return Some(current); } - match current.parent() { - Some(parent) => current = parent.to_path_buf(), - None => return None, - } + current = current.parent()?.to_path_buf(); } } diff --git a/crates/buzz-auth/src/nip_fi/startup/tests.rs b/crates/buzz-auth/src/nip_fi/startup/tests.rs index a328c8e21e..123289ee18 100644 --- a/crates/buzz-auth/src/nip_fi/startup/tests.rs +++ b/crates/buzz-auth/src/nip_fi/startup/tests.rs @@ -17,7 +17,7 @@ fn test_contract(issuer: &str) -> JwksSourceContract { fn make_offline_policy(issuer: &str) -> IssuerPolicy { IssuerPolicy::new( issuer.to_owned(), - vec![format!("https://relay.example/api")], + vec!["https://relay.example/api".to_string()], TokenClass::DedicatedNipFi, FreshnessClass::OfflineJwt, vec![JwtAlgorithm::ES256], @@ -32,7 +32,7 @@ fn make_offline_policy(issuer: &str) -> IssuerPolicy { fn make_status_policy(issuer: &str) -> IssuerPolicy { IssuerPolicy::new( issuer.to_owned(), - vec![format!("https://relay.example/api")], + vec!["https://relay.example/api".to_string()], TokenClass::DedicatedNipFi, FreshnessClass::CurrentStatus, vec![JwtAlgorithm::ES256], diff --git a/crates/buzz-media/src/validation.rs b/crates/buzz-media/src/validation.rs index 980e28d8cb..0a748ca852 100644 --- a/crates/buzz-media/src/validation.rs +++ b/crates/buzz-media/src/validation.rs @@ -56,8 +56,10 @@ pub(crate) fn looks_like_mp4_iso_bmff(bytes: &[u8]) -> bool { let major = payload[..4].try_into().ok(); major.is_some_and(|brand| MP4_BRANDS.contains(&brand)) || payload[8..] - .chunks_exact(4) - .any(|brand| MP4_BRANDS.iter().any(|candidate| brand == candidate)) + .as_chunks::<4>() + .0 + .iter() + .any(|brand| MP4_BRANDS.contains(brand)) } /// MIME types blocked from the generic file-upload path. diff --git a/crates/buzz-pairing-cli/src/main.rs b/crates/buzz-pairing-cli/src/main.rs index ca14ecebe9..2d03d594a8 100644 --- a/crates/buzz-pairing-cli/src/main.rs +++ b/crates/buzz-pairing-cli/src/main.rs @@ -327,7 +327,7 @@ async fn cmd_target(relay_override: Option, show_secret: bool) -> Result }; println!("Received {kind_label} payload!"); if show_secret { - println!("{kind_label}: {}", &*payload); + println!("{kind_label}: {}", *payload); } else { println!("(use --show-secret to display the received secret)"); } diff --git a/crates/buzz-push-gateway/src/http.rs b/crates/buzz-push-gateway/src/http.rs index ca1e18936c..f0fbfde5fe 100644 --- a/crates/buzz-push-gateway/src/http.rs +++ b/crates/buzz-push-gateway/src/http.rs @@ -311,6 +311,7 @@ struct AssertionChallenge<'a> { text: &'a str, } +#[allow(clippy::result_large_err)] // Response is the natural error type for axum handlers async fn verify_installation_assertion( s: &AppState, installation_id: uuid::Uuid, diff --git a/crates/buzz-relay/src/api/gifs.rs b/crates/buzz-relay/src/api/gifs.rs index 8dd8bdf768..c1d087dfce 100644 --- a/crates/buzz-relay/src/api/gifs.rs +++ b/crates/buzz-relay/src/api/gifs.rs @@ -223,6 +223,7 @@ pub async fn search( search_inner(state, headers, body).await.into_response() } +#[allow(clippy::result_large_err)] // Response is the natural error type for axum handlers async fn search_inner( state: Arc, headers: HeaderMap, @@ -299,6 +300,7 @@ pub async fn share( share_inner(state, headers, body).await.into_response() } +#[allow(clippy::result_large_err)] // Response is the natural error type for axum handlers async fn share_inner( state: Arc, headers: HeaderMap, diff --git a/crates/buzz-relay/src/api/git/settings.rs b/crates/buzz-relay/src/api/git/settings.rs index 00e70998a1..f11e11026b 100644 --- a/crates/buzz-relay/src/api/git/settings.rs +++ b/crates/buzz-relay/src/api/git/settings.rs @@ -5,6 +5,9 @@ //! strict NIP-98 request prevents reusable Smart HTTP credentials authorizing //! metadata changes (URL, method, payload and replay are all checked). +// Response is the natural error type for axum handlers. +#![allow(clippy::result_large_err)] + use std::sync::Arc; use axum::{ diff --git a/crates/buzz-relay/src/api/git/transport.rs b/crates/buzz-relay/src/api/git/transport.rs index 72b34bbe77..1c0c08a600 100644 --- a/crates/buzz-relay/src/api/git/transport.rs +++ b/crates/buzz-relay/src/api/git/transport.rs @@ -8,6 +8,9 @@ //! Auth: NIP-98 on all routes (clone + push). No public repos for v1. //! Transport: shells out to `git --stateless-rpc` with `env_clear()`. +// Response is the natural error type for axum handlers. +#![allow(clippy::result_large_err)] + use std::future::Future; use std::path::Path; use std::sync::Arc; @@ -4827,11 +4830,13 @@ mod off_mode_precedence_tests { const GIT_TEST_ISSUER: &str = "https://git-pack-test.issuer.invalid"; const GIT_TEST_AUDIENCE: &str = "https://git-pack-test.relay.invalid"; const GIT_TEST_KID: &str = "git-pack-test-key-1"; - const GIT_TEST_EC_PEM: &str = "-----BEGIN PRIVATE KEY-----\n\ - MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgcnxDM4EiirH9dHUE\n\ - WZc759TX4s5PAn8kO5ovXSnGxCWhRANCAARFb6ZnsfkqOOXyEhj3KBQphGKF4vTa\n\ - zhebbavbZ1ZoklqkF1cGg+jTO7rONAVEzXvXUWtV6CdDV+rybiVmFP2w\n\ - -----END PRIVATE KEY-----\n"; + const GIT_TEST_EC_PEM: &str = concat!( + "-----BEGIN PRIVATE KEY-----\n", // sadscan:disable kingfisher.privkey.2 -- test-only ES256 fixture + "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgcnxDM4EiirH9dHUE\n", + "WZc759TX4s5PAn8kO5ovXSnGxCWhRANCAARFb6ZnsfkqOOXyEhj3KBQphGKF4vTa\n", + "zhebbavbZ1ZoklqkF1cGg+jTO7rONAVEzXvXUWtV6CdDV+rybiVmFP2w\n", + "-----END PRIVATE KEY-----\n", + ); let jwks: JwkSet = serde_json::from_value(serde_json::json!({ "keys": [{ diff --git a/crates/buzz-relay/src/api/profiles.rs b/crates/buzz-relay/src/api/profiles.rs index a1c467a445..66885cf283 100644 --- a/crates/buzz-relay/src/api/profiles.rs +++ b/crates/buzz-relay/src/api/profiles.rs @@ -82,6 +82,7 @@ pub async fn search( search_inner(state, headers, body).await.into_response() } +#[allow(clippy::result_large_err)] // Response is the natural error type for axum handlers async fn search_inner( state: Arc, headers: HeaderMap, diff --git a/crates/buzz-relay/src/api/workflows.rs b/crates/buzz-relay/src/api/workflows.rs index bd33493191..957a238a80 100644 --- a/crates/buzz-relay/src/api/workflows.rs +++ b/crates/buzz-relay/src/api/workflows.rs @@ -158,6 +158,7 @@ pub async fn workflow_runs( .into_response() } +#[allow(clippy::result_large_err)] // Response is the natural error type for axum handlers async fn workflow_runs_inner( state: Arc, workflow_id: Uuid, @@ -235,6 +236,7 @@ pub async fn run_approvals( .into_response() } +#[allow(clippy::result_large_err)] // Response is the natural error type for axum handlers async fn run_approvals_inner( state: Arc, workflow_id: Uuid, diff --git a/crates/buzz-sdk/src/broker/tests.rs b/crates/buzz-sdk/src/broker/tests.rs index 14a55005ae..57031089a6 100644 --- a/crates/buzz-sdk/src/broker/tests.rs +++ b/crates/buzz-sdk/src/broker/tests.rs @@ -2602,13 +2602,8 @@ impl BrokerClient for DoubleBroker { fn block_on(future: F) -> F::Output { // A hand-rolled park-free executor: the double's future is always ready, so // one poll suffices and pulling in a runtime would be the heavier choice. - use std::task::{Context, Poll, Wake, Waker}; - struct NoopWake; - impl Wake for NoopWake { - fn wake(self: std::sync::Arc) {} - } - let waker = Waker::from(std::sync::Arc::new(NoopWake)); - let mut context = Context::from_waker(&waker); + use std::task::{Context, Poll, Waker}; + let mut context = Context::from_waker(Waker::noop()); let mut future = Box::pin(future); match future.as_mut().poll(&mut context) { Poll::Ready(output) => output, diff --git a/crates/buzz-voice/src/imported.rs b/crates/buzz-voice/src/imported.rs index 6f0ea71cad..5dbe9297f8 100644 --- a/crates/buzz-voice/src/imported.rs +++ b/crates/buzz-voice/src/imported.rs @@ -580,7 +580,7 @@ mod tests { let mono = fixture(sample_rate, seconds, amplitude); let mono_data = &mono[44..]; let mut stereo_data = Vec::with_capacity(mono_data.len() * 2); - for sample in mono_data.chunks_exact(2) { + for sample in mono_data.as_chunks::<2>().0 { stereo_data.extend_from_slice(sample); stereo_data.extend_from_slice(sample); } diff --git a/crates/buzz-voice/src/pocket_april.rs b/crates/buzz-voice/src/pocket_april.rs index 9ace5001da..43cbbd77f8 100644 --- a/crates/buzz-voice/src/pocket_april.rs +++ b/crates/buzz-voice/src/pocket_april.rs @@ -1336,8 +1336,10 @@ fn read_npy_f32(path: &Path) -> Result, String> { return Err(format!("misaligned float32 data in {}", path.display())); } Ok(data - .chunks_exact(4) - .map(|chunk| f32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]])) + .as_chunks::<4>() + .0 + .iter() + .map(|chunk| f32::from_le_bytes(*chunk)) .collect()) } diff --git a/desktop/src-tauri/src/app_state_tests.rs b/desktop/src-tauri/src/app_state_tests.rs index ceef4d3f93..8e314e75ce 100644 --- a/desktop/src-tauri/src/app_state_tests.rs +++ b/desktop/src-tauri/src/app_state_tests.rs @@ -706,8 +706,8 @@ fn present_keyring_with_mismatched_file_adopts_file_key_marker_failure_keeps_fil // so removing write permission on the dir blocks the write entirely. let dir_perms_orig = std::fs::metadata(dir.path()).unwrap().permissions(); let mut dir_perms_ro = dir_perms_orig.clone(); - // unknown_lints: the clippy lint below doesn't exist yet in the pinned - // 1.95 toolchain but does in CI's newer clippy — allow both worlds. + // unknown_lints: keep this compiling on clippy versions that predate the + // lint below, so the pinned toolchain and any newer clippy both accept it. #[allow(unknown_lints)] #[allow(clippy::permissions_set_readonly_value)] dir_perms_ro.set_readonly(true); diff --git a/desktop/src-tauri/src/commands/legacy_storage.rs b/desktop/src-tauri/src/commands/legacy_storage.rs index fa7854d023..c140e342da 100644 --- a/desktop/src-tauri/src/commands/legacy_storage.rs +++ b/desktop/src-tauri/src/commands/legacy_storage.rs @@ -81,11 +81,13 @@ fn decode_webkit_local_storage_value(bytes: &[u8]) -> Option { } if bytes.len().is_multiple_of(2) { - let has_utf16_ascii_shape = bytes.chunks_exact(2).any(|chunk| chunk[1] == 0); + let has_utf16_ascii_shape = bytes.as_chunks::<2>().0.iter().any(|chunk| chunk[1] == 0); if has_utf16_ascii_shape { let utf16: Vec = bytes - .chunks_exact(2) - .map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]])) + .as_chunks::<2>() + .0 + .iter() + .map(|chunk| u16::from_le_bytes(*chunk)) .collect(); if let Ok(value) = String::from_utf16(&utf16) { return Some(value.trim_end_matches('\0').to_string()); diff --git a/desktop/src-tauri/src/hpke_key_backup_tests.rs b/desktop/src-tauri/src/hpke_key_backup_tests.rs index 3b15964ba5..5bfa7a90bc 100644 --- a/desktop/src-tauri/src/hpke_key_backup_tests.rs +++ b/desktop/src-tauri/src/hpke_key_backup_tests.rs @@ -1,4 +1,4 @@ -use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _}; +use base64::engine::general_purpose::URL_SAFE_NO_PAD; use nostr::{Keys, SecretKey, ToBech32}; use rustls::crypto::hpke::{EncapsulatedSecret, Hpke, HpkePrivateKey}; diff --git a/desktop/src-tauri/src/huddle/latency_bench.rs b/desktop/src-tauri/src/huddle/latency_bench.rs index f928ddbce0..04ae66554d 100644 --- a/desktop/src-tauri/src/huddle/latency_bench.rs +++ b/desktop/src-tauri/src/huddle/latency_bench.rs @@ -64,8 +64,10 @@ fn read_wav_f32_48k(path: &str) -> Vec { assert!(fmt_ok, "fmt chunk missing"); let (off, len) = data.expect("data chunk missing"); bytes[off..off + len] - .chunks_exact(4) - .map(|b| f32::from_le_bytes([b[0], b[1], b[2], b[3]])) + .as_chunks::<4>() + .0 + .iter() + .map(|b| f32::from_le_bytes(*b)) .collect() } diff --git a/desktop/src-tauri/src/huddle/relay_api.rs b/desktop/src-tauri/src/huddle/relay_api.rs index 67f205c2db..8495332978 100644 --- a/desktop/src-tauri/src/huddle/relay_api.rs +++ b/desktop/src-tauri/src/huddle/relay_api.rs @@ -400,8 +400,10 @@ async fn audio_relay_pipeline(args: AudioRelayPipelineArgs) -> Result<(), String continue; // Malformed batch. } let samples: Vec = pcm_bytes - .chunks_exact(4) - .map(|b| f32::from_le_bytes([b[0], b[1], b[2], b[3]])) + .as_chunks::<4>() + .0 + .iter() + .map(|b| f32::from_le_bytes(*b)) .collect(); let mut tx = ws_tx_send.lock().await; diff --git a/desktop/src-tauri/src/huddle/stt.rs b/desktop/src-tauri/src/huddle/stt.rs index c27bf38b64..897844e103 100644 --- a/desktop/src-tauri/src/huddle/stt.rs +++ b/desktop/src-tauri/src/huddle/stt.rs @@ -832,8 +832,10 @@ fn vad_flush_allowed(ptt_mode: bool, manually_open: bool, ptt_held: bool) -> boo /// byte order, which is LE on all supported platforms. fn bytes_to_f32(bytes: &[u8]) -> Vec { bytes - .chunks_exact(4) - .map(|b| f32::from_le_bytes([b[0], b[1], b[2], b[3]])) + .as_chunks::<4>() + .0 + .iter() + .map(|b| f32::from_le_bytes(*b)) .collect() } diff --git a/desktop/src-tauri/src/managed_agents/config_bridge/reader.rs b/desktop/src-tauri/src/managed_agents/config_bridge/reader.rs index 84eec8db33..0382d4d572 100644 --- a/desktop/src-tauri/src/managed_agents/config_bridge/reader.rs +++ b/desktop/src-tauri/src/managed_agents/config_bridge/reader.rs @@ -607,7 +607,7 @@ fn build_thinking_field( let rec_native = thinking_env_var .and_then(|k| super::effort::get_ci(&record.env_vars, k)) .and_then(|v| norm(v)); - let column = record.effort_level.as_deref().and_then(&norm); + let column = record.effort_level.as_deref().and_then(norm); let rec_legacy = thinking_env_var .filter(|k| *k != LEGACY_THINKING_EFFORT_KEY) .and_then(|_| super::effort::get_ci(&record.env_vars, LEGACY_THINKING_EFFORT_KEY)) @@ -620,7 +620,7 @@ fn build_thinking_field( let glob = thinking_env_var.and_then(|k| effort_tier_alias(&tiers.global_env, k, norm, false)); let def = thinking_env_var.and_then(|k| effort_tier_alias(&tiers.definition_env, k, norm, false)); - let file = file_effort.as_deref().and_then(&norm); + let file = file_effort.as_deref().and_then(norm); // Live ACP value: normalized through the runtime CONTRACT only, never the // persisted `effort_accepted` vocabulary. The ACP running value comes from diff --git a/rust-toolchain.toml b/rust-toolchain.toml index f92020c653..99e619bb8c 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "1.95.0" +channel = "1.98.1" profile = "default"