diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index c86581355..a31b588d9 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -33,8 +33,9 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - - uses: dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4 # stable + - uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772 # master with: + toolchain: nightly-2026-08-14 components: rustfmt - uses: Swatinem/rust-cache@23869a5bd66c73db3c0ac40331f3206eb23791dc # v2.9.1 with: diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 6f8bc70bb..9ac8b32f8 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -24,7 +24,9 @@ jobs: - uses: 'actions/checkout@11d5960a326750d5838078e36cf38b85af677262' # v4 with: persist-credentials: false - - uses: 'dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4' # stable + - uses: 'dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772' # master + with: + toolchain: nightly-2026-08-14 - uses: 'Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32' # v2 with: shared-key: 'linux-stable' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e101eb184..71f261294 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -65,7 +65,7 @@ jobs: git config --global --add safe.directory /workspace curl --proto "=https" --tlsv1.2 --retry 5 --retry-delay 2 \ --retry-all-errors -sSf https://sh.rustup.rs | \ - sh -s -- -y --default-toolchain stable --target ${{ matrix.target }} + sh -s -- -y --default-toolchain nightly-2026-08-14 --target ${{ matrix.target }} source "$HOME/.cargo/env" cargo build --release --target ${{ matrix.target }} ' @@ -106,7 +106,7 @@ jobs: with: persist-credentials: false - name: Install Rust toolchain - run: rustup target add ${{ matrix.target }} + run: rustup toolchain install nightly-2026-08-14 --profile minimal --target ${{ matrix.target }} - name: Build run: cargo build --release --target ${{ matrix.target }} diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index c3166c8ed..9f6f5994e 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -242,8 +242,9 @@ jobs: - uses: 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1' # v7 with: persist-credentials: false - - uses: 'dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4' # stable + - uses: 'dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772' # master with: + toolchain: nightly-2026-08-14 components: llvm-tools-preview - uses: 'Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32' # v2 with: @@ -258,21 +259,6 @@ jobs: files: lcov.info fail_ci_if_error: false token: ${{ secrets.CODECOV_TOKEN }} - msrv: - name: MSRV (1.97) - needs: changes - if: needs.changes.outputs.code == 'true' - runs-on: ubuntu-latest - steps: - - uses: 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1' # v7 - with: - persist-credentials: false - - uses: 'dtolnay/rust-toolchain@35a842e360814583e976785eeda0bd0655cb8e83' # 1.97.0 - - uses: 'Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32' # v2 - with: - shared-key: 'linux-msrv' - save-if: ${{ github.ref == 'refs/heads/main' }} - - run: cargo build --workspace --all-targets rustdoc: name: Rustdoc needs: changes @@ -304,7 +290,6 @@ jobs: - build-windows - test-windows - coverage - - msrv - rustdoc runs-on: ubuntu-latest steps: diff --git a/Cargo.toml b/Cargo.toml index 5ac51b684..156797cec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,6 @@ homepage = "https://github.com/w0wl0lxd/n00n" readme = "README.md" keywords = ["ai", "agent", "cli", "tui", "coding"] categories = ["command-line-utilities", "development-tools"] -rust-version = "1.97" [[bin]] name = "n00n" @@ -89,7 +88,6 @@ edition = "2024" authors = ["w0wl0lxd "] license = "AGPL-3.0-only" repository = "https://github.com/w0wl0lxd/n00n" -rust-version = "1.97" [workspace.dependencies] n00n-agent = { path = "n00n-agent" } @@ -330,11 +328,14 @@ clone_on_ref_ptr = "deny" redundant_clone = "deny" manual_assert = "deny" disallowed_methods = "deny" -# These style lints suggest `unwrap_or`/`unwrap_or_default`, which are -# disallowed above; allowing them avoids clippy auto-fix introducing banned methods. +# These style lints suggest `unwrap_or`/`unwrap_or_default`/`assert_eq!`, +# which the workspace bans or does not use at scale; allowing them avoids +# clippy auto-fix introducing banned methods or mass-rewriting test asserts. unnecessary_lazy_evaluations = "allow" unnecessary_map_or = "allow" unwrap_or_default = "allow" +assert_is_empty = "allow" +result_large_err = "allow" [profile.bench] debug = true diff --git a/changelog.d/nightly-toolchain-cranelift.changed.md b/changelog.d/nightly-toolchain-cranelift.changed.md new file mode 100644 index 000000000..f4a6e9b16 --- /dev/null +++ b/changelog.d/nightly-toolchain-cranelift.changed.md @@ -0,0 +1 @@ +n00n now builds on the pinned nightly-2026-08-14 Rust toolchain instead of stable. CI toolchain steps were switched to nightly, the MSRV job was removed since the pinned-nightly policy has no minimum supported version, and the `rust-version` field was dropped from all workspace manifests accordingly. \ No newline at end of file diff --git a/n00n-acp/Cargo.toml b/n00n-acp/Cargo.toml index 1aa013254..739cecddb 100644 --- a/n00n-acp/Cargo.toml +++ b/n00n-acp/Cargo.toml @@ -6,7 +6,6 @@ edition.workspace = true license.workspace = true authors.workspace = true repository.workspace = true -rust-version.workspace = true [dependencies] n00n-agent = { workspace = true } diff --git a/n00n-acp/src/server.rs b/n00n-acp/src/server.rs index 60378816f..6d2af5e91 100644 --- a/n00n-acp/src/server.rs +++ b/n00n-acp/src/server.rs @@ -655,7 +655,7 @@ fn start_event_pump( fn next_request_id(counter: &AtomicI64) -> Option { counter - .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |id| id.checked_add(1)) + .try_update(Ordering::Relaxed, Ordering::Relaxed, |id| id.checked_add(1)) .ok() .and_then(|id| id.checked_add(1)) .map(RequestId::Number) @@ -694,12 +694,8 @@ fn no_session() -> AcpError { } fn parse_params(raw: &Value) -> Result { - serde_json::from_value( - raw.get("params") - .cloned() - .map_or(Value::Null, std::convert::identity), - ) - .map_err(|e| AcpError::invalid_params().data(json_str(&e))) + serde_json::from_value(raw.get("params").cloned().unwrap_or_else(|| Value::Null)) + .map_err(|e| AcpError::invalid_params().data(json_str(&e))) } fn json_str(e: &impl std::fmt::Display) -> Value { diff --git a/n00n-acp/src/translate.rs b/n00n-acp/src/translate.rs index e145da6af..baa1babf4 100644 --- a/n00n-acp/src/translate.rs +++ b/n00n-acp/src/translate.rs @@ -17,7 +17,7 @@ fn fenced(text: &str) -> String { .split(|c: char| c != '`') .map(str::len) .max() - .map_or(0, std::convert::identity); + .unwrap_or_else(|| 0); let fence = "`".repeat(MIN_FENCE_LEN.max(longest_backtick_run + 1)); format!("{fence}\n{text}\n{fence}") } diff --git a/n00n-agent/Cargo.toml b/n00n-agent/Cargo.toml index cc88325e8..bf0ba81a3 100644 --- a/n00n-agent/Cargo.toml +++ b/n00n-agent/Cargo.toml @@ -6,7 +6,6 @@ edition.workspace = true license.workspace = true authors.workspace = true repository.workspace = true -rust-version.workspace = true [dependencies] n00n-providers = { workspace = true } diff --git a/n00n-agent/src/agent/tool_dispatch.rs b/n00n-agent/src/agent/tool_dispatch.rs index 3aa3edd33..ccfc1e1ce 100644 --- a/n00n-agent/src/agent/tool_dispatch.rs +++ b/n00n-agent/src/agent/tool_dispatch.rs @@ -279,8 +279,7 @@ fn skill_policy_denied(name: &str, ctx: &ToolContext) -> Option { } fn is_skill_tool_call(name: &str) -> bool { - name.strip_prefix("functions.").map_or(name, |value| value) - == crate::skill_policy::SKILL_TOOL_NAME + name.strip_prefix("functions.").unwrap_or_else(|| name) == crate::skill_policy::SKILL_TOOL_NAME } fn is_subagent_failure(event: &ToolDoneEvent, ctx: &ToolContext) -> bool { @@ -366,7 +365,7 @@ async fn run_authorized( fusion_delegate_authorized: bool, ) -> ToolDoneEvent { // GPT-5.6 was likely trained on Codex sessions where tools are `functions.` - let name = name.strip_prefix("functions.").map_or(name, |value| value); + let name = name.strip_prefix("functions.").unwrap_or_else(|| name); if !ctx.tool_filter.matches(name) { return tool_done_error(id, Arc::from(name), TOOL_FILTER_DENIED.into()); } diff --git a/n00n-codegraph/Cargo.toml b/n00n-codegraph/Cargo.toml index 032e27f31..becd832c4 100644 --- a/n00n-codegraph/Cargo.toml +++ b/n00n-codegraph/Cargo.toml @@ -5,7 +5,6 @@ edition.workspace = true authors.workspace = true license.workspace = true repository.workspace = true -rust-version.workspace = true # Targets CodeGraph 1.5.0 CLI for command expansion (callers, callees, impact, affected, node, query, sync, files) # Falls back to CLI when native SQLite queries are not available diff --git a/n00n-config-macro/Cargo.toml b/n00n-config-macro/Cargo.toml index 01869f880..8048e8bae 100644 --- a/n00n-config-macro/Cargo.toml +++ b/n00n-config-macro/Cargo.toml @@ -6,7 +6,6 @@ edition.workspace = true license.workspace = true authors.workspace = true repository.workspace = true -rust-version.workspace = true [lib] proc-macro = true diff --git a/n00n-config/Cargo.toml b/n00n-config/Cargo.toml index fe56456b3..f6cf2e794 100644 --- a/n00n-config/Cargo.toml +++ b/n00n-config/Cargo.toml @@ -6,7 +6,6 @@ edition.workspace = true license.workspace = true authors.workspace = true repository.workspace = true -rust-version.workspace = true [dependencies] n00n-config-macro = { workspace = true } diff --git a/n00n-config/src/lib.rs b/n00n-config/src/lib.rs index d9b151a2d..83da65714 100644 --- a/n00n-config/src/lib.rs +++ b/n00n-config/src/lib.rs @@ -450,7 +450,7 @@ impl CompactionBuffer { match self { Self::Tokens(n) => n, Self::Percent(p) => u32::try_from(u64::from(context_window) * u64::from(p) / 100) - .map_or(u32::MAX, |v| v), + .unwrap_or_else(|_| u32::MAX), } } } @@ -990,7 +990,9 @@ impl UiConfig { splash_animation: f.splash_animation.is_none_or(|v| v), mascot: f.mascot.is_none_or(|v| v), scrollbar: f.scrollbar.is_none_or(|v| v), - flash_duration_ms: f.flash_duration_ms.map_or(DEFAULT_FLASH_DURATION_MS, |v| v), + flash_duration_ms: f + .flash_duration_ms + .unwrap_or_else(|| DEFAULT_FLASH_DURATION_MS), typewriter_ms_per_char: f .typewriter_ms_per_char .unwrap_or_else(|| DEFAULT_TYPEWRITER_MS_PER_CHAR), @@ -1066,17 +1068,17 @@ impl ToolOutputLines { let d = Self::DEFAULT; let f = f.unwrap_or_else(ToolOutputLinesFile::default); Self { - bash: f.bash.map_or(d.bash, |v| v), - code_execution: f.code_execution.map_or(d.code_execution, |v| v), - task: f.task.map_or(d.task, |v| v), - workflow: f.workflow.map_or(d.workflow, |v| v), - index: f.index.map_or(d.index, |v| v), - grep: f.grep.map_or(d.grep, |v| v), - explore: f.explore.map_or(d.explore, |v| v), - read: f.read.map_or(d.read, |v| v), - write: f.write.map_or(d.write, |v| v), - web: f.web.map_or(d.web, |v| v), - other: f.other.map_or(d.other, |v| v), + bash: f.bash.unwrap_or_else(|| d.bash), + code_execution: f.code_execution.unwrap_or_else(|| d.code_execution), + task: f.task.unwrap_or_else(|| d.task), + workflow: f.workflow.unwrap_or_else(|| d.workflow), + index: f.index.unwrap_or_else(|| d.index), + grep: f.grep.unwrap_or_else(|| d.grep), + explore: f.explore.unwrap_or_else(|| d.explore), + read: f.read.unwrap_or_else(|| d.read), + write: f.write.unwrap_or_else(|| d.write), + web: f.web.unwrap_or_else(|| d.web), + other: f.other.unwrap_or_else(|| d.other), } } @@ -1246,7 +1248,7 @@ impl AgentConfig { let fusion = if let Some(ff) = file.fusion { FusionConfig { - enabled: ff.enabled.map_or(false, |enabled| enabled), + enabled: ff.enabled.unwrap_or_else(|| false), lead_model: ff .lead_model .unwrap_or_else(|| DEFAULT_FUSION_LEAD_MODEL.to_owned()), @@ -1258,7 +1260,7 @@ impl AgentConfig { .unwrap_or_else(|| DEFAULT_FUSION_SIDEKICK_THINKING.to_owned()), sidekick_tier: ff .sidekick_tier - .map_or(crate::providers::Tier::Weak, |tier| tier), + .unwrap_or_else(|| crate::providers::Tier::Weak), } } else { FusionConfig::default() @@ -1268,10 +1270,10 @@ impl AgentConfig { no_rtk, max_output_bytes: file .max_output_bytes - .map_or(DEFAULT_MAX_OUTPUT_BYTES, |v| v), + .unwrap_or_else(|| DEFAULT_MAX_OUTPUT_BYTES), max_output_lines: file .max_output_lines - .map_or(DEFAULT_MAX_OUTPUT_LINES, |v| v), + .unwrap_or_else(|| DEFAULT_MAX_OUTPUT_LINES), max_continuation_turns: file .max_continuation_turns .unwrap_or_else(|| DEFAULT_MAX_CONTINUATION_TURNS), @@ -1357,19 +1359,19 @@ impl ProviderConfig { default_model: f.default_model, connect_timeout: Duration::from_secs( f.connect_timeout_secs - .map_or(DEFAULT_CONNECT_TIMEOUT_SECS, |v| v), + .unwrap_or_else(|| DEFAULT_CONNECT_TIMEOUT_SECS), ), low_speed_timeout: Duration::from_secs( f.low_speed_timeout_secs - .map_or(DEFAULT_LOW_SPEED_TIMEOUT_SECS, |v| v), + .unwrap_or_else(|| DEFAULT_LOW_SPEED_TIMEOUT_SECS), ), stream_timeout: Duration::from_secs( f.stream_timeout_secs - .map_or(DEFAULT_STREAM_TIMEOUT_SECS, |v| v), + .unwrap_or_else(|| DEFAULT_STREAM_TIMEOUT_SECS), ), openai_coding_plan_slots: f .openai_coding_plan_slots - .map_or(DEFAULT_OPENAI_CODING_PLAN_SLOTS, |slots| slots), + .unwrap_or_else(|| DEFAULT_OPENAI_CODING_PLAN_SLOTS), } } @@ -1434,11 +1436,15 @@ impl Default for StorageConfig { impl StorageConfig { fn from_file(f: &StorageFileConfig) -> Self { Self { - max_log_bytes: f.max_log_bytes_mb.map_or(DEFAULT_MAX_LOG_BYTES_MB, |v| v) * 1024 * 1024, - max_log_files: f.max_log_files.map_or(DEFAULT_MAX_LOG_FILES, |v| v), + max_log_bytes: f + .max_log_bytes_mb + .unwrap_or_else(|| DEFAULT_MAX_LOG_BYTES_MB) + * 1024 + * 1024, + max_log_files: f.max_log_files.unwrap_or_else(|| DEFAULT_MAX_LOG_FILES), input_history_size: f .input_history_size - .map_or(DEFAULT_INPUT_HISTORY_SIZE, |v| v), + .unwrap_or_else(|| DEFAULT_INPUT_HISTORY_SIZE), } } } @@ -1753,7 +1759,7 @@ fn build_permissions( global: &PermissionsFileConfig, project: &PermissionsFileConfig, ) -> PermissionsConfig { - let global_default = global.default.map_or(DefaultEffect::Prompt, |v| v); + let global_default = global.default.unwrap_or_else(|| DefaultEffect::Prompt); let default = if let Some(d) = project.default && d != DefaultEffect::Allow { diff --git a/n00n-daemon/Cargo.toml b/n00n-daemon/Cargo.toml index aa5c6bd88..f31f1f1bc 100644 --- a/n00n-daemon/Cargo.toml +++ b/n00n-daemon/Cargo.toml @@ -6,7 +6,6 @@ description = "On-device agent control plane: registry, UDS NDJSON protocol, wor license.workspace = true authors.workspace = true repository.workspace = true -rust-version.workspace = true [dependencies] n00n-storage = { workspace = true } diff --git a/n00n-docgen/Cargo.toml b/n00n-docgen/Cargo.toml index a4b9c18a3..b370c2e3b 100644 --- a/n00n-docgen/Cargo.toml +++ b/n00n-docgen/Cargo.toml @@ -6,7 +6,6 @@ edition.workspace = true authors.workspace = true license.workspace = true repository.workspace = true -rust-version.workspace = true [[bin]] name = "n00n-docgen" diff --git a/n00n-git/Cargo.toml b/n00n-git/Cargo.toml index 673468ac1..b2ee5135f 100644 --- a/n00n-git/Cargo.toml +++ b/n00n-git/Cargo.toml @@ -5,7 +5,6 @@ edition.workspace = true authors.workspace = true license.workspace = true repository.workspace = true -rust-version.workspace = true description = "Native git operations using gix/gitoxide for n00n" [[bin]] diff --git a/n00n-highlight/Cargo.toml b/n00n-highlight/Cargo.toml index 6d7350e5f..cf6c19944 100644 --- a/n00n-highlight/Cargo.toml +++ b/n00n-highlight/Cargo.toml @@ -6,7 +6,6 @@ edition.workspace = true authors.workspace = true license.workspace = true repository.workspace = true -rust-version.workspace = true [dependencies] serde_json = { workspace = true } diff --git a/n00n-highlight/src/lib.rs b/n00n-highlight/src/lib.rs index 8affcf0b1..4f873163d 100644 --- a/n00n-highlight/src/lib.rs +++ b/n00n-highlight/src/lib.rs @@ -95,7 +95,7 @@ pub fn syntax_for_path(path: &str) -> &'static SyntaxReference { .ok() .flatten() .unwrap_or_else(|| { - let ext = path.rsplit('.').next().map_or(path, |ext| ext); + let ext = path.rsplit('.').next().unwrap_or_else(|| path); syntax_for_token(ext) }) } diff --git a/n00n-interpreter/Cargo.toml b/n00n-interpreter/Cargo.toml index c71ddabc4..ecbf8a522 100644 --- a/n00n-interpreter/Cargo.toml +++ b/n00n-interpreter/Cargo.toml @@ -6,7 +6,6 @@ edition.workspace = true license.workspace = true authors.workspace = true repository.workspace = true -rust-version.workspace = true [dependencies] monty = { workspace = true } diff --git a/n00n-lua-macro/Cargo.toml b/n00n-lua-macro/Cargo.toml index 763135d14..09dc0b5db 100644 --- a/n00n-lua-macro/Cargo.toml +++ b/n00n-lua-macro/Cargo.toml @@ -6,7 +6,6 @@ edition.workspace = true license.workspace = true authors.workspace = true repository.workspace = true -rust-version.workspace = true [lib] proc-macro = true diff --git a/n00n-lua/Cargo.toml b/n00n-lua/Cargo.toml index 7c419b0b5..1856f9a89 100644 --- a/n00n-lua/Cargo.toml +++ b/n00n-lua/Cargo.toml @@ -6,7 +6,6 @@ edition.workspace = true authors.workspace = true license.workspace = true repository.workspace = true -rust-version.workspace = true [dependencies] arc-swap = { workspace = true } diff --git a/n00n-lua/src/api/agent.rs b/n00n-lua/src/api/agent.rs index a3c7d72f6..eb0bd0ab5 100644 --- a/n00n-lua/src/api/agent.rs +++ b/n00n-lua/src/api/agent.rs @@ -365,12 +365,12 @@ fn usage_cost( try_pair!(breakdown.get::>("cache_read_tokens").map_err( |error| format!("invalid breakdown field 'cache_read_tokens': {error}") )) - .map_or(0, |tokens| tokens); + .unwrap_or_else(|| 0); let cache_write = try_pair!(breakdown.get::>("cache_write_tokens").map_err( |error| format!("invalid breakdown field 'cache_write_tokens': {error}") )) - .map_or(0, |tokens| tokens); + .unwrap_or_else(|| 0); let fast = try_pair!( breakdown .get::>("fast") @@ -482,8 +482,12 @@ fn tools(lua: &Lua, ctx: mlua::UserDataRef, opts: Table) -> LuaResult> = opts.get("only")?; let except: Option> = opts.get("except")?; let tool_exclusions = except.clone(); - let include_mcp: bool = opts.get::>("include_mcp")?.map_or(true, |v| v); - let workflow: bool = opts.get::>("workflow")?.map_or(false, |v| v); + let include_mcp: bool = opts + .get::>("include_mcp")? + .unwrap_or_else(|| true); + let workflow: bool = opts + .get::>("workflow")? + .unwrap_or_else(|| false); let spec_str: Option = opts.get("spec")?; let parsed = spec_str @@ -668,7 +672,7 @@ async fn session( let local_tools_tbl: Option = opts.get("local_tools")?; let include_mcp = opts .get::>("include_mcp")? - .map_or(true, |value| value); + .unwrap_or_else(|| true); let excluded_tools = merged_tool_exclusions( tools_val.as_ref(), opts.get::>>("except")?, @@ -692,7 +696,7 @@ async fn session( }; let requested_fast: bool = opts .get::>("fast")? - .map_or(agent_ctx.opts.fast, |v| v); + .unwrap_or_else(|| agent_ctx.opts.fast); let (model, provider): (Model, Arc) = if let Some(ref spec) = model_spec { diff --git a/n00n-lua/src/api/autocmd.rs b/n00n-lua/src/api/autocmd.rs index 1ac3f5f6a..8df9293ef 100644 --- a/n00n-lua/src/api/autocmd.rs +++ b/n00n-lua/src/api/autocmd.rs @@ -151,7 +151,7 @@ fn parse_string_or_seq(value: Value, what: &str) -> LuaResult> { fn create_autocmd(lua: &Lua, #[ctx] plugin: Arc, event: Value, opts: Table) -> LuaResult { let events = parse_string_or_seq(event, "event")?; let callback: Function = opts.get("callback")?; - let once: bool = opts.get("once").map_or(false, |v| v); + let once = opts.get::>("once")?.unwrap_or_else(|| false); let patterns = match opts.get::("pattern")? { Value::Nil => None, v => Some(parse_string_or_seq(v, "pattern")?), diff --git a/n00n-lua/src/api/fn.rs b/n00n-lua/src/api/fn.rs index 565c9f30b..84a5c3717 100644 --- a/n00n-lua/src/api/fn.rs +++ b/n00n-lua/src/api/fn.rs @@ -195,7 +195,13 @@ impl JobStore { thread::Builder::new() .name("job-wait".into()) .spawn(move || { - let code = child.wait().map_or(-1, |s| s.code().map_or(-1, |c| c)); + let code = match child.wait() { + Ok(status) => status.code().unwrap_or_else(|| -1), + Err(error) => { + tracing::error!(error = %error, "job wait failed"); + -1 + } + }; if let Some(h) = stdout_handle { let _ = h.join(); } diff --git a/n00n-lua/src/api/tool.rs b/n00n-lua/src/api/tool.rs index eae2bb601..bbf4b0a59 100644 --- a/n00n-lua/src/api/tool.rs +++ b/n00n-lua/src/api/tool.rs @@ -1538,11 +1538,11 @@ fn optional_nonnegative_integer(table: &mlua::Table, field: &str) -> Result Result { let fresh_input = optional_nonnegative_integer(table, FRESH_INPUT_TOKENS_FIELD)?; let cache_read = - optional_nonnegative_integer(table, CACHE_READ_TOKENS_FIELD)?.map_or(0, |value| value); + optional_nonnegative_integer(table, CACHE_READ_TOKENS_FIELD)?.unwrap_or_else(|| 0); let cache_write = - optional_nonnegative_integer(table, CACHE_WRITE_TOKENS_FIELD)?.map_or(0, |value| value); + optional_nonnegative_integer(table, CACHE_WRITE_TOKENS_FIELD)?.unwrap_or_else(|| 0); let input = optional_nonnegative_integer(table, INPUT_TOKENS_FIELD)?; - let output = optional_nonnegative_integer(table, OUTPUT_TOKENS_FIELD)?.map_or(0, |value| value); + let output = optional_nonnegative_integer(table, OUTPUT_TOKENS_FIELD)?.unwrap_or_else(|| 0); let cached = cache_read .checked_add(cache_write) .ok_or_else(|| "tool usage cached input token categories overflow".to_owned())?; diff --git a/n00n-lua/src/api/ui/mod.rs b/n00n-lua/src/api/ui/mod.rs index acb120d4d..7d8ccb754 100644 --- a/n00n-lua/src/api/ui/mod.rs +++ b/n00n-lua/src/api/ui/mod.rs @@ -1132,8 +1132,7 @@ mod tests { && line .get::
(1) .and_then(|s| s.get::(2)) - .ok() - .is_some_and(|s| s == STYLE_CODE_BAR)) + .is_ok_and(|s| s == STYLE_CODE_BAR)) .then_some(line) }) .expect("code bar line"); diff --git a/n00n-markdown/Cargo.toml b/n00n-markdown/Cargo.toml index c0e26e62c..c2bcfbc93 100644 --- a/n00n-markdown/Cargo.toml +++ b/n00n-markdown/Cargo.toml @@ -6,7 +6,6 @@ edition.workspace = true authors.workspace = true license.workspace = true repository.workspace = true -rust-version.workspace = true [dependencies] n00n-highlight = { workspace = true } diff --git a/n00n-markdown/src/lib.rs b/n00n-markdown/src/lib.rs index 81256a95e..2f3bc5caf 100644 --- a/n00n-markdown/src/lib.rs +++ b/n00n-markdown/src/lib.rs @@ -371,8 +371,8 @@ fn is_separator_row(line: &str) -> bool { fn parse_table_cells(line: &str) -> Vec { let t = line.trim(); - let inner = t.strip_prefix('|').map_or(t, |s| s); - let inner = inner.strip_suffix('|').map_or(inner, |s| s); + let inner = t.strip_prefix('|').unwrap_or_else(|| t); + let inner = inner.strip_suffix('|').unwrap_or_else(|| inner); let bytes = inner.as_bytes(); let mut cells = Vec::new(); @@ -397,7 +397,7 @@ fn parse_table_cells(line: &str) -> Vec { current = String::new(); i += 1; } else { - let ch = inner[i..].chars().next().map_or('\0', |c| c); + let ch = inner[i..].chars().next().unwrap_or_else(|| '\0'); current.push(ch); i += ch.len_utf8(); } diff --git a/n00n-markdown/src/render.rs b/n00n-markdown/src/render.rs index 114a5703a..71c6fb9dc 100644 --- a/n00n-markdown/src/render.rs +++ b/n00n-markdown/src/render.rs @@ -402,7 +402,7 @@ fn ensure_blank_line(lines: &mut Vec) { fn fit_width(text: &str, max_width: usize) -> usize { let mut width = 0; for (i, ch) in text.char_indices() { - let cw = UnicodeWidthChar::width(ch).map_or(0, |w| w); + let cw = UnicodeWidthChar::width(ch).unwrap_or_else(|| 0); if width + cw > max_width { return i; } @@ -514,7 +514,7 @@ fn constrain_col_widths(col_widths: &mut [usize], available: usize) { } let mut excess = col_widths.iter().sum::().saturating_sub(available); while excess > 0 { - let max_w = col_widths.iter().copied().max().map_or(0, |w| w); + let max_w = col_widths.iter().copied().max().unwrap_or_else(|| 0); if max_w <= MIN_COL_WIDTH { break; } @@ -558,7 +558,7 @@ fn wrap_spans(spans: Vec, max_width: usize) -> Vec> { } result.push(mem::take(&mut current)); remaining = max_width; - text = text.strip_prefix(' ').map_or(text, |s| s); + text = text.strip_prefix(' ').unwrap_or_else(|| text); continue; } let (take, skip) = if fits < text.len() { @@ -626,7 +626,7 @@ fn render_table( .iter() .map(std::vec::Vec::len) .max() - .map_or(0, |len| len); + .unwrap_or_else(|| 0); if col_count == 0 { return Vec::new(); } @@ -687,7 +687,7 @@ fn render_table( .iter() .map(std::vec::Vec::len) .max() - .map_or(1, |len| len); + .unwrap_or_else(|| 1); let row_emphasis = if header { Emphasis::BOLD } else { diff --git a/n00n-providers/Cargo.toml b/n00n-providers/Cargo.toml index 000186add..92bdaa07a 100644 --- a/n00n-providers/Cargo.toml +++ b/n00n-providers/Cargo.toml @@ -6,7 +6,6 @@ edition.workspace = true license.workspace = true authors.workspace = true repository.workspace = true -rust-version.workspace = true build = "build.rs" [dependencies] diff --git a/n00n-providers/src/model.rs b/n00n-providers/src/model.rs index 64a751d64..0b0c218ee 100644 --- a/n00n-providers/src/model.rs +++ b/n00n-providers/src/model.rs @@ -336,7 +336,7 @@ impl Model { let model_id = self .id .strip_prefix(OPENAI_MODEL_PREFIX) - .map_or(self.id.as_str(), std::convert::identity); + .unwrap_or_else(|| self.id.as_str()); (model_id.starts_with(GPT_MODEL_PREFIX) && !model_id.contains(GPT_CODEX_MARKER)) .then_some(model_id) } @@ -440,9 +440,7 @@ impl Model { let version = version_and_suffix .split_once('-') .map_or(version_and_suffix, |(version, _)| version); - let (major, minor) = version - .split_once('.') - .map_or((version, "0"), std::convert::identity); + let (major, minor) = version.split_once('.').unwrap_or_else(|| (version, "0")); let (Ok(major), Ok(minor)) = (major.parse::(), minor.parse::()) else { return false; }; diff --git a/n00n-providers/src/providers/cursor/mod.rs b/n00n-providers/src/providers/cursor/mod.rs index bf093da95..4e64810bd 100644 --- a/n00n-providers/src/providers/cursor/mod.rs +++ b/n00n-providers/src/providers/cursor/mod.rs @@ -265,7 +265,7 @@ impl Cursor { } let text = if result.text.is_empty() { - result.result.map_or(String::new(), |t| t) + result.result.unwrap_or_else(String::new) } else { result.text }; @@ -683,7 +683,7 @@ async fn handle_thinking_event( let full = value .get("text") .and_then(serde_json::Value::as_str) - .map_or("", |v| v) + .unwrap_or_else(|| "") .to_string(); if full.is_empty() { return Ok(()); diff --git a/n00n-providers/src/providers/devin.rs b/n00n-providers/src/providers/devin.rs index c645765eb..4a85937c8 100644 --- a/n00n-providers/src/providers/devin.rs +++ b/n00n-providers/src/providers/devin.rs @@ -332,7 +332,7 @@ fn encode_devin_tools(tools: &Value) -> Result>, AgentError> { .get("strict") .or_else(|| function.get("strict")) .and_then(Value::as_bool) - .map_or(false, std::convert::identity); + .unwrap_or_else(|| false); encoded.push(encode_chat_tool_definition(&ChatToolDefinition { name: name.to_string(), description: description.map_or(String::new(), std::string::ToString::to_string), diff --git a/n00n-providers/src/providers/openai/platform.rs b/n00n-providers/src/providers/openai/platform.rs index 9dbd41726..f2827c333 100644 --- a/n00n-providers/src/providers/openai/platform.rs +++ b/n00n-providers/src/providers/openai/platform.rs @@ -4035,7 +4035,6 @@ mod tests { #[test] #[allow(clippy::large_futures)] - #[allow(clippy::result_large_err)] fn token_refresh_during_new_socket_handshake_reconnects_before_create() { smol::block_on(async { let listener = smol::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); diff --git a/n00n-redact/Cargo.toml b/n00n-redact/Cargo.toml index 60ec6fda8..22460a988 100644 --- a/n00n-redact/Cargo.toml +++ b/n00n-redact/Cargo.toml @@ -6,7 +6,6 @@ edition.workspace = true authors.workspace = true license.workspace = true repository.workspace = true -rust-version.workspace = true [dependencies] serde_json = { workspace = true } diff --git a/n00n-redact/src/lib.rs b/n00n-redact/src/lib.rs index 4f2c9d708..7284e22f6 100644 --- a/n00n-redact/src/lib.rs +++ b/n00n-redact/src/lib.rs @@ -350,7 +350,7 @@ fn contains_url_userinfo_credentials(value: &str) -> bool { let authority = &value[scheme_end + 3..]; let authority_end = authority .find(['/', '?', '#']) - .map_or(authority.len(), |end| end); + .unwrap_or_else(|| authority.len()); authority[..authority_end] .rsplit_once('@') .and_then(|(userinfo, _)| userinfo.split_once(':')) @@ -594,7 +594,7 @@ mod tests { "user": "bob", "note": jwt, "nested": { "data": format!("sk-{}", "a".repeat(48)) }, - "list": [format!("AKIA0123456789ABCDEF")], + "list": ["AKIA0123456789ABCDEF".to_string()], }); let redacted = redact_json_value_for_log(&value); assert_eq!(redacted["user"], "bob"); diff --git a/n00n-search/Cargo.toml b/n00n-search/Cargo.toml index 6fc17f305..53186fda2 100644 --- a/n00n-search/Cargo.toml +++ b/n00n-search/Cargo.toml @@ -6,7 +6,6 @@ edition.workspace = true authors.workspace = true license.workspace = true repository.workspace = true -rust-version.workspace = true [lints] workspace = true diff --git a/n00n-semble/Cargo.toml b/n00n-semble/Cargo.toml index 233259270..4e233d35b 100644 --- a/n00n-semble/Cargo.toml +++ b/n00n-semble/Cargo.toml @@ -5,7 +5,6 @@ edition.workspace = true authors.workspace = true license.workspace = true repository.workspace = true -rust-version.workspace = true [lints] workspace = true diff --git a/n00n-smell/Cargo.toml b/n00n-smell/Cargo.toml index 12c0d9377..194110c52 100644 --- a/n00n-smell/Cargo.toml +++ b/n00n-smell/Cargo.toml @@ -5,7 +5,6 @@ edition.workspace = true authors.workspace = true license.workspace = true repository.workspace = true -rust-version.workspace = true description = "Persistent code-smell and comment index for n00n" [[bin]] diff --git a/n00n-smell/src/lib.rs b/n00n-smell/src/lib.rs index ec2bedd92..70df42ab1 100644 --- a/n00n-smell/src/lib.rs +++ b/n00n-smell/src/lib.rs @@ -579,7 +579,7 @@ pub fn format_results(results: &[SearchResult]) -> String { .content .lines() .next() - .map_or(result.content.as_str(), std::convert::identity) + .unwrap_or_else(|| result.content.as_str()) ) }) .collect::>() diff --git a/n00n-storage/Cargo.toml b/n00n-storage/Cargo.toml index faba3f235..a172e9697 100644 --- a/n00n-storage/Cargo.toml +++ b/n00n-storage/Cargo.toml @@ -6,7 +6,6 @@ edition.workspace = true license.workspace = true authors.workspace = true repository.workspace = true -rust-version.workspace = true [dependencies] getrandom = { workspace = true } diff --git a/n00n-storage/src/auth.rs b/n00n-storage/src/auth.rs index 08e42419a..d543ebfc5 100644 --- a/n00n-storage/src/auth.rs +++ b/n00n-storage/src/auth.rs @@ -68,9 +68,10 @@ impl ProviderCredentials { #[must_use] pub fn now_millis() -> u64 { - SystemTime::now() - .duration_since(UNIX_EPOCH) - .map_or(0, |d| u64::try_from(d.as_millis()).map_or(u64::MAX, |v| v)) + SystemTime::now().duration_since(UNIX_EPOCH).map_or_else( + |_| 0, + |d| u64::try_from(d.as_millis()).unwrap_or_else(|_| u64::MAX), + ) } fn auth_path(dir: &StateDir, filename: &str) -> PathBuf { diff --git a/n00n-storage/src/sessions.rs b/n00n-storage/src/sessions.rs index 1fc8142d7..08d56c63e 100644 --- a/n00n-storage/src/sessions.rs +++ b/n00n-storage/src/sessions.rs @@ -2458,7 +2458,7 @@ where None } }; - let record_tag = tag.as_deref().map_or("?", |t| t); + let record_tag = tag.as_deref().unwrap_or_else(|| "?"); warn!( path = %path.display(), error = %error, diff --git a/n00n-token-profile/Cargo.toml b/n00n-token-profile/Cargo.toml index d1a921f4b..bd6cf96a3 100644 --- a/n00n-token-profile/Cargo.toml +++ b/n00n-token-profile/Cargo.toml @@ -5,7 +5,6 @@ edition.workspace = true authors.workspace = true license.workspace = true repository.workspace = true -rust-version.workspace = true description = "Offline cold-start token profiling and CI regression gates for n00n" publish = false diff --git a/n00n-ui/Cargo.toml b/n00n-ui/Cargo.toml index 99d55bf9c..e93179f11 100644 --- a/n00n-ui/Cargo.toml +++ b/n00n-ui/Cargo.toml @@ -6,7 +6,6 @@ edition.workspace = true license.workspace = true authors.workspace = true repository.workspace = true -rust-version.workspace = true [dependencies] n00n-agent = { workspace = true } diff --git a/n00n-ui/src/app/session.rs b/n00n-ui/src/app/session.rs index 252d531de..5a448d0a2 100644 --- a/n00n-ui/src/app/session.rs +++ b/n00n-ui/src/app/session.rs @@ -27,7 +27,7 @@ use crate::agent::{Delivery, QueuedMessage}; const INITIAL_STATE_REVISION: u64 = 0; pub(super) fn plugin_state_identity(session: &AppSession) -> SessionIdentity { - let root_id = session.meta.root_session_id.map_or(session.id, |root| root); + let root_id = session.meta.root_session_id.unwrap_or_else(|| session.id); let session_id = SessionRef::from_id(session.id); if root_id == session.id { SessionIdentity::root(session_id) diff --git a/n00n-ui/src/components/command.rs b/n00n-ui/src/components/command.rs index c43c778ff..386cc4fb5 100644 --- a/n00n-ui/src/components/command.rs +++ b/n00n-ui/src/components/command.rs @@ -478,12 +478,12 @@ impl CommandPalette { .iter() .map(|item| self.item_name(item).len()) .max() - .map_or(0, |v| v); + .unwrap_or_else(|| 0); let max_desc = filtered .iter() .map(|item| self.item_description(item).len()) .max() - .map_or(0, |v| v); + .unwrap_or_else(|| 0); let popup_width = cast::usize_to_u16(PAD + max_name + GAP + max_desc + PAD); let popup = Rect { diff --git a/n00n-ui/src/components/file_picker.rs b/n00n-ui/src/components/file_picker.rs index 6e78eea4a..e9b0eaae8 100644 --- a/n00n-ui/src/components/file_picker.rs +++ b/n00n-ui/src/components/file_picker.rs @@ -495,7 +495,7 @@ fn render_search(frame: &mut Frame, area: Rect, s: &Session) { let cursor_byte = TextBuffer::char_to_byte(&query, s.search.x()); let (before, rest) = query.split_at(cursor_byte); let mut chars = rest.chars(); - let cursor_char = chars.next().map_or(' ', |c| c); + let cursor_char = chars.next().unwrap_or_else(|| ' '); let after = chars.as_str(); let mut spans = vec![super::chevron_span()]; @@ -532,7 +532,7 @@ fn build_highlighted_line<'a>( let mut width = 0usize; for (i, ch) in text.chars().enumerate() { - let cw = ch.width().map_or(0, |w| w); + let cw = ch.width().unwrap_or_else(|| 0); if width + cw > max_width { break; } diff --git a/n00n-ui/src/components/input.rs b/n00n-ui/src/components/input.rs index 49a620408..477dc64a2 100644 --- a/n00n-ui/src/components/input.rs +++ b/n00n-ui/src/components/input.rs @@ -351,9 +351,9 @@ impl InputBox { let cursor_col: usize = line .chars() .take(self.buffer.x()) - .map(|c| c.width().map_or(1, |w| w)) + .map(|c| c.width().unwrap_or_else(|| 1)) .sum(); - cast::usize_to_u16(cursor_col.checked_div(ew).map_or(0, |v| v)) + cast::usize_to_u16(cursor_col.checked_div(ew).unwrap_or_else(|| 0)) }; lines_above + wrap_row diff --git a/n00n-ui/src/components/list_picker.rs b/n00n-ui/src/components/list_picker.rs index 9fd05c3d0..8d27a8fd6 100644 --- a/n00n-ui/src/components/list_picker.rs +++ b/n00n-ui/src/components/list_picker.rs @@ -725,7 +725,7 @@ fn render_list( (true, true) => { let s = t .item_selected - .fg(t.accent.fg.map_or(Color::default(), |c| c)); + .fg(t.accent.fg.unwrap_or_else(Color::default)); (s, theme::dim_style(s, 0.4)) } (true, false) => (t.item_selected, t.item_selected), diff --git a/n00n-ui/src/components/messages/mod.rs b/n00n-ui/src/components/messages/mod.rs index 4c9439091..5b524552e 100644 --- a/n00n-ui/src/components/messages/mod.rs +++ b/n00n-ui/src/components/messages/mod.rs @@ -571,7 +571,7 @@ impl MessagesPanel { .expanded_tools .get(&inst_id) .copied() - .map_or(SectionFlags::default(), |v| v); + .unwrap_or_else(SectionFlags::default); let tl = build_instructions_lines(blocks, self.viewport_width, exp.output); if let Some(seg_idx) = self.cache.find_instructions(parent_id) { @@ -685,7 +685,7 @@ impl MessagesPanel { .expanded_tools .get(tool_id) .copied() - .map_or(SectionFlags::default(), |v| v); + .unwrap_or_else(SectionFlags::default); if !seg.truncation.any() && !exp.any() { return false; } @@ -959,7 +959,7 @@ impl MessagesPanel { .expanded_tools .get(&tool_id) .copied() - .map_or(SectionFlags::default(), |v| v); + .unwrap_or_else(SectionFlags::default); if !truncation.any() && !exp.any() { return false; } @@ -1680,7 +1680,7 @@ impl MessagesPanel { .expanded_tools .get(tool_id) .copied() - .map_or(SectionFlags::default(), |v| v); + .unwrap_or_else(SectionFlags::default); let rctx = self.rctx(); let tl = Self::build_tool_segment_lines(msg, status, &rctx, exp); diff --git a/n00n-ui/src/event_loop.rs b/n00n-ui/src/event_loop.rs index f051c355d..5da973766 100644 --- a/n00n-ui/src/event_loop.rs +++ b/n00n-ui/src/event_loop.rs @@ -434,7 +434,7 @@ impl SpawnCtx { let identity = session_identity(&session) .map_err(|error| eyre!("invalid session identity: {error}"))?; if let Some(handle) = &self.lua_event_handle { - let root_id = session.meta.root_session_id.map_or(session.id, |root| root); + let root_id = session.meta.root_session_id.unwrap_or_else(|| session.id); if !self.hydrated_roots.borrow().contains(&root_id) { let root_snapshot = if root_id == session.id { session.meta.state_snapshot.clone() @@ -1207,7 +1207,7 @@ impl<'t> EventLoop<'t> { .session .meta .root_session_id - .map_or(self.sessions[idx].id(), |root| root); + .unwrap_or_else(|| self.sessions[idx].id()); capture_session_plugin_state(&handle, &mut self.sessions[idx].app.state.session)?; if root_id == self.sessions[idx].id() { return Ok(()); diff --git a/n00n-ui/src/theme.rs b/n00n-ui/src/theme.rs index b5b789c92..8a572b263 100644 --- a/n00n-ui/src/theme.rs +++ b/n00n-ui/src/theme.rs @@ -607,8 +607,7 @@ impl Theme { let style = |key: &str| -> Style { ui.get(key) - .map(|d| resolve_style(d, &palette)) - .map_or(Style::default(), |s| s) + .map_or_else(Style::default, |d| resolve_style(d, &palette)) }; let derived_color = |ui_key: &str, scopes: &[&str]| -> Color { @@ -858,7 +857,7 @@ impl Theme { let s = style(primary); if s == Style::default() { style(fallback_base) - .fg(style(accent).fg.map_or(Color::default(), |c| c)) + .fg(style(accent).fg.unwrap_or_else(Color::default)) .add_modifier(Modifier::BOLD) } else { s diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 46e006fb4..d1db44210 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "stable" +channel = "nightly-2026-08-14" components = ["clippy", "rustfmt", "rust-src", "rust-analyzer"] profile = "minimal" targets = ["x86_64-pc-windows-msvc"] diff --git a/src/cmd/agent.rs b/src/cmd/agent.rs index 8cda1d6c8..c81b21815 100644 --- a/src/cmd/agent.rs +++ b/src/cmd/agent.rs @@ -101,7 +101,7 @@ fn print_control_response(resp: &ControlResponse, json: bool) -> Result<()> { a.id, a.backend, a.status, - a.title.as_deref().map_or("", |t| t) + a.title.as_deref().unwrap_or_else(|| "") ); } } @@ -1325,7 +1325,7 @@ fn print_agent_table(agents: &[AgentRecord]) { agent.id, agent.backend, agent.status, - agent.title.as_deref().map_or("", |title| title) + agent.title.as_deref().unwrap_or_else(|| "") ); } } @@ -1381,8 +1381,8 @@ pub fn list_client( " {} - {} - {} - {}", agent.id, agent.status, - agent.model.as_deref().map_or("?", |m| m), - agent.title.as_deref().map_or("", |t| t) + agent.model.as_deref().unwrap_or_else(|| "?"), + agent.title.as_deref().unwrap_or_else(|| "") ); } diff --git a/src/cmd/tui.rs b/src/cmd/tui.rs index 910a73464..94ae2d67e 100644 --- a/src/cmd/tui.rs +++ b/src/cmd/tui.rs @@ -542,7 +542,7 @@ fn handle_reload( *warnings = new_warnings; let focused = f.min(tabs.len() - 1); tracing::info!( - elapsed_ms = u64::try_from(started.elapsed().as_millis()).map_or(u64::MAX, |ms| ms), + elapsed_ms = u64::try_from(started.elapsed().as_millis()).unwrap_or_else(|_| u64::MAX), tabs = tabs.len(), "reload: rebuilt plugins and config" ); diff --git a/src/cmd/tui_bridge.rs b/src/cmd/tui_bridge.rs index c18bb553a..4443341c5 100644 --- a/src/cmd/tui_bridge.rs +++ b/src/cmd/tui_bridge.rs @@ -173,7 +173,7 @@ fn build_team_resume_prompt(run_info: &Value) -> ControlResult { let mode = run_info .get("mode") .and_then(Value::as_str) - .map_or("autonomous", |m| m); + .unwrap_or_else(|| "autonomous"); let args = serde_json::json!({ "goal": "resume", "resume": run_id,