Skip to content

Fix Clippy warnings and run Clippy in CI#156

Merged
aleyan merged 7 commits intomainfrom
clippy
Apr 29, 2026
Merged

Fix Clippy warnings and run Clippy in CI#156
aleyan merged 7 commits intomainfrom
clippy

Conversation

@aleyan
Copy link
Copy Markdown
Owner

@aleyan aleyan commented Apr 26, 2026

Summary by CodeRabbit

  • Chores

    • Pinned CI actions and toolchain components; added explicit Rust toolchain config and minimum Rust version.
    • Added a Makefile lint target (runs formatter and clippy).
    • Updated test container images to a specific Rust version.
  • Refactor

    • Simplified conditional/control-flow patterns across parsers, task discovery, server, and allowlist logic.
    • Replaced a handwritten default with a derived Default.
  • Tests

    • Updated various test helpers and assertions for clarity and consistency.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 26, 2026

📝 Walkthrough

Walkthrough

Refactors nested conditionals into compound guards across many parsers and core modules, derives Default for a DTO, broadens a path helper signature, consolidates MCP/server JSON handling, pins Rust/toolchain versions and adds clippy to CI, adds a make lint target, and updates test Docker images to Rust 1.91.1.

Changes

Cohort / File(s) Summary
CI & Toolchain
/.github/workflows/rust.yml, /.github/workflows/integration.yml, rust-toolchain.toml, Makefile, Cargo.toml, tests/Dockerfile.builder, tests/docker_unit/Dockerfile
Pins GH Action commits, sets Rust toolchain to 1.91.1 (toolchain file + Dockerfiles), ensures rustfmt and clippy are installed, replaces cargo fmt --check with make lint, adds lint target, and adds rust-version = "1.91.1" to Cargo.toml.
Allowlist & MCP
src/allowlist.rs, src/mcp/allowlist.rs, src/commands/mcp.rs, src/mcp/server.rs, src/mcp/dto.rs
Flattens nested if/if let into compound guards, consolidates cache-hit check, passes owned serde_json::json!(...) into responses, changes some status checks to is_ok_and/is_some, and derives Default for ListTasksArgs (removes manual impl).
Commands & UI
src/commands/list.rs, src/commands/init.rs, src/prompt.rs
Adjusts task-column padding to round up with div_ceil(5) * 5, simplifies disambiguation/colouring checks, narrows test helper signatures, and changes a TUI test options container to a fixed-size array.
Task discovery & types
src/task_discovery.rs, src/types.rs
Uses or_default() and is_some_and/is_ok_and predicates, flattens discovery guards, and broadens serialize_path to accept &Path.
Parsers (various)
src/parsers/...
parse_cmake.rs, parse_github_actions.rs, parse_gradle.rs, parse_makefile.rs, parse_package_json.rs, parse_pom_xml.rs, parse_pyproject_toml.rs, parse_taskfile.rs, parse_travis_ci.rs, parse_justfile.rs
Replaces nested if/if let chains with single compound conditions, precompiles/reuses regexes where applicable, simplifies string/format usage, standardizes serde_yaml::Mapping::get key forms, and updates tests to use any/boolean-style assertions.
Tests & small edits
tests/..., various unit tests across crate
Removes redundant casts (pid as u32), adjusts ownership/borrowing in test calls (e.g., act_tasks[0]), replaces get(0) with .first(), and simplifies assertions (e.g., assert!(...) / assert!(!...)).

Sequence Diagram(s)

(Skipped — changes are refactor/CI/tooling-focused and do not introduce a new multi-component sequential feature.)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰
I nibbled nested ifs to one neat line,
Pinned the toolchain, made the CI shine,
Clippy watched as I hopped through code,
Formatting tidy on every road,
A happy rabbit, linting all the time!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: fixing Clippy warnings and enabling Clippy in CI, which is reflected across the entire changeset including code refactorings, CI workflow updates, and toolchain pinning.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch clippy

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (6)
src/parsers/parse_package_json.rs (1)

23-37: ⚠️ Potential issue | 🔴 Critical

Same rustfmt over-indentation; CI cargo fmt --check fails.

🛠️ Proposed fix
-    if let Some(scripts) = json.get("scripts")
-        && let Some(scripts_obj) = scripts.as_object() {
-            for (name, cmd) in scripts_obj {
-                tasks.push(Task {
-                    name: name.clone(),
-                    file_path: path.clone(),
-                    definition_type: TaskDefinitionType::PackageJson,
-                    runner: runner.clone(),
-                    source_name: name.clone(),
-                    description: cmd.as_str().map(|s| s.to_string()),
-                    shadowed_by: None,
-                    disambiguated_name: None,
-                });
-            }
-        }
+    if let Some(scripts) = json.get("scripts")
+        && let Some(scripts_obj) = scripts.as_object()
+    {
+        for (name, cmd) in scripts_obj {
+            tasks.push(Task {
+                name: name.clone(),
+                file_path: path.clone(),
+                definition_type: TaskDefinitionType::PackageJson,
+                runner: runner.clone(),
+                source_name: name.clone(),
+                description: cmd.as_str().map(|s| s.to_string()),
+                shadowed_by: None,
+                disambiguated_name: None,
+            });
+        }
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/parsers/parse_package_json.rs` around lines 23 - 37, The code block in
parse_package_json.rs is over-indented causing cargo fmt --check to fail;
reformat the if-let chain and its body to match rustfmt style (align the `if let
Some(scripts) = json.get("scripts") && let Some(scripts_obj) =
scripts.as_object()` and the following block braces), and fix indentation of the
`for (name, cmd) in scripts_obj { ... }` and the `tasks.push(Task { ... })`
initializer (which includes fields like name, file_path, definition_type,
runner, source_name, description, shadowed_by, disambiguated_name). After
adjusting indentation, run cargo fmt to ensure the file is properly formatted.
src/parsers/parse_pom_xml.rs (1)

87-141: ⚠️ Potential issue | 🔴 Critical

Same rustfmt over-indentation across the entire collapsed block; CI cargo fmt --check fails.

The whole inner body (L89–141) is still indented to the old double-if let depth and must shift one level left after collapsing. Running cargo fmt --all reformats it; the structure should become:

if let Some(build_node) = root.children().find(|n| n.has_tag_name("build"))
    && let Some(plugins_node) = build_node.children().find(|n| n.has_tag_name("plugins"))
{
    for plugin in plugins_node.children().filter(|n| n.has_tag_name("plugin")) {
        // ... body indented one level shallower than current
    }
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/parsers/parse_pom_xml.rs` around lines 87 - 141, The block using the
combined `if let` (matching `build_node` and `plugins_node` found from
`root.children()`) is over-indented after collapsing the condition; fix by
placing the opening brace on its own line after the combined condition for `if
let Some(build_node) = root.children().find(|n| n.has_tag_name("build")) && let
Some(plugins_node) = build_node.children().find(|n| n.has_tag_name("plugins"))`
and shift the entire inner body (the `for plugin in plugins_node.children()...`
loop and its contents that produce `Task` entries) one level left so it aligns
as shown in the review, then run `cargo fmt --all` or `cargo fmt --check` to
ensure rustfmt accepts the change.
src/parsers/parse_pyproject_toml.rs (3)

16-34: ⚠️ Potential issue | 🟠 Major

Fix the rustfmt failure flagged by CI.

The pipeline reports cargo fmt --all -- --check failures for both this UV-scripts block (lines 15–43) and the poe-tasks block at line 71. The let-chain bodies are using cascading 4-space indentation (if let ... \n && let ... \n && let ... { ... }), which rustfmt rewrites to single-step indentation with the opening brace on its own line. Please run cargo fmt --all and amend.

Expected shape (apply analogously to the poe block at lines 71–107):

🎨 Proposed format
-    if let Some(project) = toml.get("project")
-        && let Some(scripts) = project.get("scripts")
-            && let Some(scripts_table) = scripts.as_table()
-                && (cfg!(test) || check_path_executable("uv").is_some()) {
-                    for (name, cmd) in scripts_table {
-                        let description = cmd.as_str().map(|s| format!("python script: {}", s));
-
-                        tasks.push(Task {
-                            name: name.clone(),
-                            file_path: path.to_path_buf(),
-                            definition_type: TaskDefinitionType::PyprojectToml,
-                            runner: TaskRunner::PythonUv,
-                            source_name: name.clone(),
-                            description,
-                            shadowed_by: None,
-                            disambiguated_name: None,
-                        });
-                    }
-                }
+    if let Some(project) = toml.get("project")
+        && let Some(scripts) = project.get("scripts")
+        && let Some(scripts_table) = scripts.as_table()
+        && (cfg!(test) || check_path_executable("uv").is_some())
+    {
+        for (name, cmd) in scripts_table {
+            let description = cmd.as_str().map(|s| format!("python script: {}", s));
+
+            tasks.push(Task {
+                name: name.clone(),
+                file_path: path.to_path_buf(),
+                definition_type: TaskDefinitionType::PyprojectToml,
+                runner: TaskRunner::PythonUv,
+                source_name: name.clone(),
+                description,
+                shadowed_by: None,
+                disambiguated_name: None,
+            });
+        }
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/parsers/parse_pyproject_toml.rs` around lines 16 - 34, Run rustfmt to fix
the formatting: reformat the let-chain if conditions so the opening brace is on
its own line and indentation matches rustfmt style (e.g. transform the cascading
indents used in the if let chain that checks toml.get("project") ->
project.get("scripts") -> scripts.as_table() -> (cfg!(test) ||
check_path_executable("uv").is_some())). Update the same formatting for the
poe-tasks block mentioned later; then run cargo fmt --all and amend the commit.
Locate the block that constructs Task with TaskRunner::PythonUv in
parse_pyproject_toml.rs and the corresponding poe-tasks block and let rustfmt
rewrite the brace and indentation to the canonical style.

45-67: ⚠️ Potential issue | 🔴 Critical

Operator-precedence bug: missing parentheses change the gating semantics for Poetry scripts.

On line 51 the condition is:

if cfg!(test) || check_path_executable("poetry").is_some() && poetry_lock_exists {

Because && binds tighter than ||, this parses as:

cfg!(test) || (check_path_executable("poetry").is_some() && poetry_lock_exists)

Compare this with the parallel UV (line 19) and Poe (line 79) branches, which correctly keep the (cfg!(test) || check_path_executable(...).is_some()) group parenthesized. Judging from the tests (e.g. the comment "Create a poetry.lock file so that the poetry scripts branch is triggered" in test_parse_both_uv_and_poetry_scripts) and the symmetry with the other branches, the intended condition was:

(cfg!(test) || check_path_executable("poetry").is_some()) && poetry_lock_exists

Under the new code, in test builds the poetry_lock_exists check is bypassed entirely (since cfg!(test) short-circuits the ||), which silently weakens the gating that the existing tests are written to assert. In non-test builds the two forms happen to be equivalent, so this won't fail at runtime — but it will mask regressions in tests and is almost certainly not what was intended by a clippy-driven refactor.

🔧 Proposed fix
-        if let Some(scripts) = poetry.get("scripts")
-            && let Some(scripts_table) = scripts.as_table() {
-                let poetry_lock_exists = path
-                    .parent()
-                    .map(|dir| dir.join("poetry.lock").exists())
-                    .unwrap_or(false);
-                if cfg!(test) || check_path_executable("poetry").is_some() && poetry_lock_exists {
+        if let Some(scripts) = poetry.get("scripts")
+            && let Some(scripts_table) = scripts.as_table()
+        {
+            let poetry_lock_exists = path
+                .parent()
+                .map(|dir| dir.join("poetry.lock").exists())
+                .unwrap_or(false);
+            if (cfg!(test) || check_path_executable("poetry").is_some()) && poetry_lock_exists {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/parsers/parse_pyproject_toml.rs` around lines 45 - 67, The conditional
that gates adding Poetry scripts uses cfg!(test),
check_path_executable("poetry"), and poetry_lock_exists but lacks parentheses so
&& binds tighter than ||; update the if condition in parse_pyproject_toml.rs
(the block that inspects poetry.get("scripts") and iterates scripts_table) to
parenthesize the test or executable check together so that the existence of
poetry.lock (poetry_lock_exists) is required in addition to either cfg!(test) or
check_path_executable("poetry").is_some(); adjust the condition around
cfg!(test) and check_path_executable("poetry") accordingly to match the
semantics used for the UV/Poe branches.

71-107: 🧹 Nitpick | 🔵 Trivial

Readability: nested if let inside a let-chain is hard to follow.

The poe block combines (a) a let-chain if let Some(poe) = ... && let Some(poe_section) = ..., (b) an inner if let Some(tasks_table) = if let Some(inner) = poe_section.get("tasks") { inner.as_table() } else { poe_section.as_table() }, and (c) a trailing && (cfg!(test) || check_path_executable("poe").is_some()) — all in one expression. Lifting the table selection into a let (or a small helper) makes the gating obvious and matches the style of the other two branches.

♻️ Suggested shape
if let Some(tool) = toml.get("tool")
    && let Some(poe_section) = tool.get("poe")
{
    let tasks_table = poe_section
        .get("tasks")
        .and_then(|inner| inner.as_table())
        .or_else(|| poe_section.as_table());

    if let Some(tasks_table) = tasks_table
        && (cfg!(test) || check_path_executable("poe").is_some())
    {
        for (name, task_def) in tasks_table {
            // ...
        }
    }
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/parsers/parse_pyproject_toml.rs` around lines 71 - 107, The nested
let-chain and inline conditional that selects tasks_table makes the poe parsing
hard to read; refactor by extracting the table selection into a separate let
binding (e.g., let tasks_table = poe_section.get("tasks").and_then(|t|
t.as_table()).or_else(|| poe_section.as_table());) then use a simple if let
Some(tasks_table) = tasks_table && (cfg!(test) ||
check_path_executable("poe").is_some()) to gate the for loop that pushes Task
entries (symbols to locate: poe_section, tasks_table, check_path_executable,
Task, TaskRunner::PythonPoe); this keeps the logic identical but much clearer
and consistent with the other branches.
src/task_discovery.rs (1)

820-851: ⚠️ Potential issue | 🟠 Major

Same fmt + MSRV concern as the GitHub Actions block; also consider extracting the closure.

cargo fmt is failing here (pipeline error at line 822) — the body is indented several levels too deep after the wrapped && let Some(extension) = … && extension == "sh" head. After running cargo fmt --all, this whole branch may format more cleanly as a filter_map/for over entries.flatten().filter(...) to avoid the awkward let-chain entirely:

♻️ Optional alternative without let-chains
 fn discover_shell_script_tasks(dir: &Path, discovered: &mut DiscoveredTasks) {
-    if let Ok(entries) = fs::read_dir(dir) {
-        for entry in entries.flatten() {
-            let path = entry.path();
-            if path.is_file()
-                && let Some(extension) = path.extension()
-                    && extension == "sh" {
-                        // Use file stem (without extension) for task name and disambiguation
-                        let name = path
-                            .file_stem()
-                            .unwrap_or_default()
-                            .to_string_lossy()
-                            .to_string();
-                        ...
-                    }
-        }
-    }
+    let Ok(entries) = fs::read_dir(dir) else { return };
+    for entry in entries.flatten() {
+        let path = entry.path();
+        if !path.is_file() || path.extension().is_none_or(|ext| ext != "sh") {
+            continue;
+        }
+        let name = path.file_stem().unwrap_or_default().to_string_lossy().to_string();
+        let source_name = path.file_name().unwrap_or_default().to_string_lossy().to_string();
+        discovered.tasks.push(Task { /* … */ });
+    }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/task_discovery.rs` around lines 820 - 851, The let-chain condition causes
cargo fmt/MSRV issues; replace the nested if-let with an iterator filter_map or
filter + for to avoid the chained `&& let` and keep formatting stable: iterate
over entries.flatten(), map each entry to its path, then use path.is_file() and
path.extension().and_then(|e| e.to_str()) == Some("sh") to select scripts, build
the task using the same fields (name from file_stem, source_name from file_name,
TaskDefinitionType::ShellScript, TaskRunner::ShellScript, shadowed_by via
check_shadowing(&name)) and push into discovered.tasks; optionally extract the
task-construction block into a small helper/closure (e.g., a function that
returns Option<Task>) to keep the loop concise and readable.
♻️ Duplicate comments (1)
src/mcp/allowlist.rs (1)

38-41: ⚠️ Potential issue | 🟠 Major

Same fmt failures + let-chain MSRV concern as src/allowlist.rs.

Pipeline errors at lines 36 and 57. The Deny branch and the triple-chained Task branch mirror the patterns in src/allowlist.rs; the same simplifications (entry.scope == AllowScope::Deny, and an is_some_and for the Task branch) apply here and would remove the let-chain dependency on Rust 1.88 entirely. After applying, run cargo fmt --all.

Also applies to: 58-62

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/mcp/allowlist.rs` around lines 38 - 41, Replace the let-chain patterns in
src/mcp/allowlist.rs: change the "if let AllowScope::Deny = entry.scope &&
self.path_matches(&task.file_path, &entry.path, true)" style to a plain equality
check (entry.scope == AllowScope::Deny) combined with the path match, and for
the triple-chained Task branch (the block around lines 58-62) replace the
nested/multi-condition pattern with an Option::is_some_and(...) check on the
Task option to remove the let-chain dependency; after making these changes run
cargo fmt --all to fix formatting.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/allowlist.rs`:
- Around line 90-96: The AllowScope::Task match arm uses a chained let-binding
inside an && expression which is hard to read and breaks fmt/MSRV; replace the
chained condition with a flattened nested check: first test
path_matches(&task.file_path, &entry.path, false), then use an explicit if let
Some(ref tasks) = entry.tasks (or if let Some(tasks) = &entry.tasks) and inside
that check tasks.contains(&task.name) and return Ok((true, false)) when all
conditions hold; update the AllowScope::Task arm accordingly referencing
AllowScope::Task, path_matches, entry.tasks, task.file_path, and task.name.
- Around line 71-74: The if-statement uses a let-chain (if let AllowScope::Deny
= entry.scope && path_matches(...)) which fails fmt and requires Rust ≥1.88;
change it to a non-let-chain form using the PartialEq on AllowScope (e.g.,
compare entry.scope == AllowScope::Deny combined with
path_matches(&task.file_path, &entry.path, true)) so the condition is equivalent
but fmt-friendly and MSRV-safe, and apply the same swap in the analogous check
in the MCP allowlist code (the entry.scope / path_matches usage).

In `@src/commands/init.rs`:
- Around line 73-77: The collapsed conditional using "if let Some(parent) =
config_path.parent() && !parent.exists()" has its block over-indented; reduce
the block body (the fs::create_dir_all(...) call and its map_err(...) handling)
to a single indentation level under the if to satisfy rustfmt. Apply the same
correction for similar collapsed if-let && patterns in the functions handling
parse_cmake, parse_package_json, parse_pom_xml, parse_gradle, and the mcp
command so their blocks are not doubly-indented, then run "cargo fmt --all" to
normalize formatting.

In `@src/commands/mcp.rs`:
- Around line 172-176: The block using the combined condition "if let
Some(parent) = config_path.parent() && !parent.exists()" is over-indented — the
body (the fs::create_dir_all(...) call and its map_err closure referencing
editor.name()) should be indented only one block level under the if, not at the
deeper nested-if level; fix by reformatting the block so the lines after the
condition align with standard rust indentation (move fs::create_dir_all(...) and
.map_err(...) left to the same indentation level directly under the if-let
condition) so rustfmt/cargo fmt --check passes.

In `@src/mcp/dto.rs`:
- Around line 88-89: Merge the two adjacent derive attributes into one by adding
Default into the existing #[derive(Debug, Clone, PartialEq, Serialize,
Deserialize, JsonSchema)] list so it becomes a single #[derive(...)] attribute
(remove the separate #[derive(Default)]); update the derive on the struct that
currently has those two lines to restore idiomatic single-derive formatting and
satisfy cargo fmt --check.

In `@src/mcp/server.rs`:
- Around line 320-323: Rustfmt is failing for the let-chain conditional; update
the if-let chain so the opening brace is on its own line and the return is
dedented. Specifically, in the block that checks cache.as_ref(),
entry.cached_at.elapsed() and self.task_cache_ttl (referencing variables cache,
entry, cached_at, task_cache_ttl and the returned entry.discovered.clone()),
rewrite the conditional so the `{` is on a new line and the `return
entry.discovered.clone();` is indented one level inside the block to match
rustfmt expectations.

In `@src/parsers/parse_cmake.rs`:
- Around line 58-61: The if-let chain is mis-indented and fails cargo fmt;
change the block around comment_pattern.captures(target_block) so it conforms to
rustfmt by either putting the entire guard on one line (if let
Some(comment_captures) = comment_pattern.captures(target_block) && let
Some(comment) = comment_captures.get(1) { ... }) or by switching to nested if
lets (if let Some(comment_captures) = comment_pattern.captures(target_block) {
if let Some(comment) = comment_captures.get(1) { description =
comment.as_str().to_string(); } }) while referencing comment_pattern, captures,
target_block, comment_captures, comment and description to locate the code.

In `@src/parsers/parse_gradle.rs`:
- Around line 134-135: Replace the unnecessary allocations for
description_single_quote_pattern and description_double_quote_pattern in
parse_gradle.rs by using &'static str literals (remove the .to_string()) since
they are only passed to format! as &str; update any variable types/uses if
annotated to accept &str so the rest of the code (e.g., where format!(...)
consumes these patterns) continues to compile without allocating a String.
- Around line 138-166: The three nested `if let` chains that extract task
descriptions (the blocks building Regex with task_pattern +
description_single_quote_pattern, task_pattern +
description_double_quote_pattern, and the kotlin_pattern variable) have
over-indented bodies causing cargo fmt failures; collapse each multi-clause `if
let Ok(regex) = ... && let Some(caps) = regex.captures(content) && let
Some(desc) = caps.get(1)` so that the returned `Some(desc.as_str().to_string())`
is indented one level inside the `if` (not triple-nested), ensuring the `if`
body aligns with the canonical formatting, then run `cargo fmt --all` (or `cargo
fmt -- --check` to verify) to apply canonical formatting.

In `@src/parsers/parse_pyproject_toml.rs`:
- Around line 16-19: Add an explicit minimum Rust version to Cargo.toml by
adding rust-version = "1.88" under the [package] section so the repository
documents the MSRV required for let-chain usage (used in parse_pyproject_toml.rs
with the let-chain expression), ensuring CI/builds on older toolchains fail
clearly; update the Cargo.toml [package] metadata accordingly and commit the
change.

In `@src/parsers/parse_travis_ci.rs`:
- Line 37: Replace the redundant allocations where
config_map.get(Value::String("...".to_string())) is used: change those calls to
config_map.get("jobs") (and the other string keys used similarly) to avoid
creating a new String each call; locate uses of config_map.get with
Value::String in parse_travis_ci.rs (e.g., the pattern if let
Some(Value::Mapping(jobs_map)) =
config_map.get(Value::String("jobs".to_string()))) and replace the
Value::String("...".to_string()) argument with a plain &str like "jobs" (repeat
for the other occurrences noted).

---

Outside diff comments:
In `@src/parsers/parse_package_json.rs`:
- Around line 23-37: The code block in parse_package_json.rs is over-indented
causing cargo fmt --check to fail; reformat the if-let chain and its body to
match rustfmt style (align the `if let Some(scripts) = json.get("scripts") &&
let Some(scripts_obj) = scripts.as_object()` and the following block braces),
and fix indentation of the `for (name, cmd) in scripts_obj { ... }` and the
`tasks.push(Task { ... })` initializer (which includes fields like name,
file_path, definition_type, runner, source_name, description, shadowed_by,
disambiguated_name). After adjusting indentation, run cargo fmt to ensure the
file is properly formatted.

In `@src/parsers/parse_pom_xml.rs`:
- Around line 87-141: The block using the combined `if let` (matching
`build_node` and `plugins_node` found from `root.children()`) is over-indented
after collapsing the condition; fix by placing the opening brace on its own line
after the combined condition for `if let Some(build_node) =
root.children().find(|n| n.has_tag_name("build")) && let Some(plugins_node) =
build_node.children().find(|n| n.has_tag_name("plugins"))` and shift the entire
inner body (the `for plugin in plugins_node.children()...` loop and its contents
that produce `Task` entries) one level left so it aligns as shown in the review,
then run `cargo fmt --all` or `cargo fmt --check` to ensure rustfmt accepts the
change.

In `@src/parsers/parse_pyproject_toml.rs`:
- Around line 16-34: Run rustfmt to fix the formatting: reformat the let-chain
if conditions so the opening brace is on its own line and indentation matches
rustfmt style (e.g. transform the cascading indents used in the if let chain
that checks toml.get("project") -> project.get("scripts") -> scripts.as_table()
-> (cfg!(test) || check_path_executable("uv").is_some())). Update the same
formatting for the poe-tasks block mentioned later; then run cargo fmt --all and
amend the commit. Locate the block that constructs Task with
TaskRunner::PythonUv in parse_pyproject_toml.rs and the corresponding poe-tasks
block and let rustfmt rewrite the brace and indentation to the canonical style.
- Around line 45-67: The conditional that gates adding Poetry scripts uses
cfg!(test), check_path_executable("poetry"), and poetry_lock_exists but lacks
parentheses so && binds tighter than ||; update the if condition in
parse_pyproject_toml.rs (the block that inspects poetry.get("scripts") and
iterates scripts_table) to parenthesize the test or executable check together so
that the existence of poetry.lock (poetry_lock_exists) is required in addition
to either cfg!(test) or check_path_executable("poetry").is_some(); adjust the
condition around cfg!(test) and check_path_executable("poetry") accordingly to
match the semantics used for the UV/Poe branches.
- Around line 71-107: The nested let-chain and inline conditional that selects
tasks_table makes the poe parsing hard to read; refactor by extracting the table
selection into a separate let binding (e.g., let tasks_table =
poe_section.get("tasks").and_then(|t| t.as_table()).or_else(||
poe_section.as_table());) then use a simple if let Some(tasks_table) =
tasks_table && (cfg!(test) || check_path_executable("poe").is_some()) to gate
the for loop that pushes Task entries (symbols to locate: poe_section,
tasks_table, check_path_executable, Task, TaskRunner::PythonPoe); this keeps the
logic identical but much clearer and consistent with the other branches.

In `@src/task_discovery.rs`:
- Around line 820-851: The let-chain condition causes cargo fmt/MSRV issues;
replace the nested if-let with an iterator filter_map or filter + for to avoid
the chained `&& let` and keep formatting stable: iterate over entries.flatten(),
map each entry to its path, then use path.is_file() and
path.extension().and_then(|e| e.to_str()) == Some("sh") to select scripts, build
the task using the same fields (name from file_stem, source_name from file_name,
TaskDefinitionType::ShellScript, TaskRunner::ShellScript, shadowed_by via
check_shadowing(&name)) and push into discovered.tasks; optionally extract the
task-construction block into a small helper/closure (e.g., a function that
returns Option<Task>) to keep the loop concise and readable.

---

Duplicate comments:
In `@src/mcp/allowlist.rs`:
- Around line 38-41: Replace the let-chain patterns in src/mcp/allowlist.rs:
change the "if let AllowScope::Deny = entry.scope &&
self.path_matches(&task.file_path, &entry.path, true)" style to a plain equality
check (entry.scope == AllowScope::Deny) combined with the path match, and for
the triple-chained Task branch (the block around lines 58-62) replace the
nested/multi-condition pattern with an Option::is_some_and(...) check on the
Task option to remove the let-chain dependency; after making these changes run
cargo fmt --all to fix formatting.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: bb33fb3a-841c-48f5-bc3e-e76c5c52e1fa

📥 Commits

Reviewing files that changed from the base of the PR and between 16bbeaa and a85b255.

📒 Files selected for processing (19)
  • .github/workflows/rust.yml
  • src/allowlist.rs
  • src/commands/init.rs
  • src/commands/list.rs
  • src/commands/mcp.rs
  • src/mcp/allowlist.rs
  • src/mcp/dto.rs
  • src/mcp/server.rs
  • src/parsers/parse_cmake.rs
  • src/parsers/parse_github_actions.rs
  • src/parsers/parse_gradle.rs
  • src/parsers/parse_makefile.rs
  • src/parsers/parse_package_json.rs
  • src/parsers/parse_pom_xml.rs
  • src/parsers/parse_pyproject_toml.rs
  • src/parsers/parse_taskfile.rs
  • src/parsers/parse_travis_ci.rs
  • src/task_discovery.rs
  • src/types.rs

Comment thread src/allowlist.rs Outdated
Comment thread src/allowlist.rs
Comment on lines 90 to 96
AllowScope::Task => {
if path_matches(&task.file_path, &entry.path, false) {
if let Some(ref tasks) = entry.tasks {
if tasks.contains(&task.name) {
if path_matches(&task.file_path, &entry.path, false)
&& let Some(ref tasks) = entry.tasks
&& tasks.contains(&task.name) {
return Ok((true, false));
}
}
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Triple let-chain is hard to follow; flatten it.

Beyond the fmt failure (pipeline error at line 90) and MSRV-1.88 requirement, the binding let Some(ref tasks) = entry.tasks chained inside && with two other booleans is not particularly readable. A small refactor avoids the let-chain altogether:

♻️ Suggested simplification
             AllowScope::Task => {
-                if path_matches(&task.file_path, &entry.path, false)
-                    && let Some(ref tasks) = entry.tasks
-                        && tasks.contains(&task.name) {
-                            return Ok((true, false));
-                        }
+                let task_listed = entry
+                    .tasks
+                    .as_ref()
+                    .is_some_and(|tasks| tasks.contains(&task.name));
+                if task_listed && path_matches(&task.file_path, &entry.path, false) {
+                    return Ok((true, false));
+                }
             }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
AllowScope::Task => {
if path_matches(&task.file_path, &entry.path, false) {
if let Some(ref tasks) = entry.tasks {
if tasks.contains(&task.name) {
if path_matches(&task.file_path, &entry.path, false)
&& let Some(ref tasks) = entry.tasks
&& tasks.contains(&task.name) {
return Ok((true, false));
}
}
}
}
AllowScope::Task => {
let task_listed = entry
.tasks
.as_ref()
.is_some_and(|tasks| tasks.contains(&task.name));
if task_listed && path_matches(&task.file_path, &entry.path, false) {
return Ok((true, false));
}
}
🧰 Tools
🪛 GitHub Actions: Rust CI

[error] 90-90: cargo fmt --all -- --check failed. Formatting diff detected in allowlist.rs for AllowScope::Task branch (indentation/brace placement). Run 'cargo fmt --all' to apply rustfmt formatting.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/allowlist.rs` around lines 90 - 96, The AllowScope::Task match arm uses a
chained let-binding inside an && expression which is hard to read and breaks
fmt/MSRV; replace the chained condition with a flattened nested check: first
test path_matches(&task.file_path, &entry.path, false), then use an explicit if
let Some(ref tasks) = entry.tasks (or if let Some(tasks) = &entry.tasks) and
inside that check tasks.contains(&task.name) and return Ok((true, false)) when
all conditions hold; update the AllowScope::Task arm accordingly referencing
AllowScope::Task, path_matches, entry.tasks, task.file_path, and task.name.

Comment thread src/commands/init.rs Outdated
Comment thread src/commands/mcp.rs Outdated
Comment thread src/mcp/dto.rs Outdated
Comment thread src/parsers/parse_cmake.rs Outdated
Comment on lines +134 to +135
let description_single_quote_pattern = r"description\s+'([^']*)'".to_string();
let description_double_quote_pattern = r#"description\s+"([^"]*)""#.to_string();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Drop the unnecessary .to_string() allocations.

These are &'static str literals that are only ever interpolated into format!(...), which accepts &str. Allocating a String here is wasteful and looks like a clippy nudge worth taking while you're cleaning lints.

♻️ Proposed refactor
-    let description_single_quote_pattern = r"description\s+'([^']*)'".to_string();
-    let description_double_quote_pattern = r#"description\s+"([^"]*)""#.to_string();
+    let description_single_quote_pattern = r"description\s+'([^']*)'";
+    let description_double_quote_pattern = r#"description\s+"([^"]*)""#;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let description_single_quote_pattern = r"description\s+'([^']*)'".to_string();
let description_double_quote_pattern = r#"description\s+"([^"]*)""#.to_string();
let description_single_quote_pattern = r"description\s+'([^']*)'";
let description_double_quote_pattern = r#"description\s+"([^"]*)""#;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/parsers/parse_gradle.rs` around lines 134 - 135, Replace the unnecessary
allocations for description_single_quote_pattern and
description_double_quote_pattern in parse_gradle.rs by using &'static str
literals (remove the .to_string()) since they are only passed to format! as
&str; update any variable types/uses if annotated to accept &str so the rest of
the code (e.g., where format!(...) consumes these patterns) continues to compile
without allocating a String.

Comment thread src/parsers/parse_gradle.rs Outdated
Comment thread src/parsers/parse_pyproject_toml.rs Outdated

// Extract jobs from the configuration
if let Some(Value::Mapping(jobs_map)) = config_map.get(&Value::String("jobs".to_string())) {
if let Some(Value::Mapping(jobs_map)) = config_map.get(Value::String("jobs".to_string())) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

LGTM — clippy::needless_borrow fix applied uniformly.

Equivalent behavior since serde_yaml::mapping::Index is implemented for Value.

Optional: Each call still allocates a new String via "…".to_string(). If you wanted a follow-up clippy/perf pass, serde_yaml::Mapping::get also accepts &str directly, e.g. config_map.get("jobs"), which avoids the per-call allocation entirely. Not in scope for this PR.

Also applies to: 60-60, 64-64, 69-69, 133-133, 138-138, 144-144

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/parsers/parse_travis_ci.rs` at line 37, Replace the redundant allocations
where config_map.get(Value::String("...".to_string())) is used: change those
calls to config_map.get("jobs") (and the other string keys used similarly) to
avoid creating a new String each call; locate uses of config_map.get with
Value::String in parse_travis_ci.rs (e.g., the pattern if let
Some(Value::Mapping(jobs_map)) =
config_map.get(Value::String("jobs".to_string()))) and replace the
Value::String("...".to_string()) argument with a plain &str like "jobs" (repeat
for the other occurrences noted).

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/mcp/server.rs (1)

2620-2625: ⚠️ Potential issue | 🟡 Minor

Empty test body — this test now passes unconditionally.

After removing the placeholder assert!(true), test_concurrency_limit_in_task_start contains only comments, so it asserts nothing and just inflates the green-test count. The behavior it claims to cover is already exercised by test_concurrency_limit_enforcement against the JobManager directly.

Either delete this test or wire it up to actually drive task_start against a server with a constrained JobManagerConfig (e.g., via a new new_with_allowlist_and_job_manager-style test hook) so the concurrency error path through task_start itself is covered.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/mcp/server.rs` around lines 2620 - 2625, The empty test
test_concurrency_limit_in_task_start should either be removed or implemented to
actually exercise the task_start path under a constrained JobManager; implement
it by creating a server instance configured with a low-concurrency
JobManagerConfig (add/use a test helper like new_with_allowlist_and_job_manager
or new_with_job_manager_config) and drive task_start RPCs until the JobManager
returns the concurrency error, then assert the expected error is returned;
alternatively, delete the empty test if coverage via
test_concurrency_limit_enforcement is sufficient.
Makefile (1)

1-1: 🧹 Nitpick | 🔵 Trivial

Add lint to the .PHONY declaration.

The new lint target produces no file named lint, so it should be declared phony to guarantee the recipe always runs (and to prevent any future file collision from silently skipping it). Several existing targets (format, _builder, inspect_mcp, test_long) are also missing from .PHONY — worth fixing in the same spot.

Proposed change
-.PHONY: build tests tests_integration test_unit test_noinit test_mcp test_zsh test_bash test_fish test_pwsh run install builder publish
+.PHONY: build lint tests tests_integration test_unit test_noinit test_mcp test_zsh test_bash test_fish test_pwsh run install _builder builder publish format inspect_mcp test_long

Also applies to: 13-15

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Makefile` at line 1, Update the .PHONY declaration to include the new lint
target and other non-file-producing targets to avoid accidental skipping: add
lint plus format, _builder, inspect_mcp, and test_long to the existing .PHONY
list so targets like build/tests/run/install/builder/publish remain correct and
the lint recipe always executes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/rust.yml:
- Around line 26-32: The cache key in the GitHub Actions step (the `key` field
currently set to `${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}`)
does not include the Rust toolchain version and can reuse artifacts built with a
different rustc; update the `key` to also hash the rust-toolchain file (or
Cargo.toml rust-version) so toolchain bumps invalidate the cache—specifically
modify the `key` expression to include `hashFiles('rust-toolchain.toml')` (or
`hashFiles('**/Cargo.toml')` targeting rust-version) alongside the existing
Cargo.lock hash to ensure `target/` and registry caches are version-aware.

In `@Cargo.toml`:
- Line 4: Update the Cargo.toml rust-version field from the patch-pinned value
to a major.minor only value: change the rust-version declaration (the
"rust-version" entry in Cargo.toml) from "1.91.1" to "1.91" so Cargo's version
check accepts Rust 1.91.0 while your rust-toolchain.toml continues to pin the
exact build for CI/contributors.

In `@rust-toolchain.toml`:
- Around line 1-3: The toolchain is pinned to an exact patch via channel =
"1.91.1"; decide whether you want exact-patch or minor pinning and update the
channel string accordingly (e.g., change channel = "1.91.1" to channel = "1.91"
to allow patch updates), and ensure components = ["rustfmt","clippy"] remains
unchanged; update the channel value in the rust toolchain manifest where the
[toolchain] section and channel key are defined.

In `@src/parsers/parse_pyproject_toml.rs`:
- Around line 46-69: The boolean expression around cfg!(test),
check_path_executable("poetry") and poetry_lock_exists has ambiguous precedence;
change the guard so the poetry_lock_exists check is applied unconditionally by
rewriting the condition to: poetry_lock_exists && (cfg!(test) ||
check_path_executable("poetry").is_some()), and update the if in
parse_pyproject_toml.rs (the block that builds Task with
TaskRunner::PythonPoetry) accordingly; if you intentionally want tests to bypass
the lock check instead, add explicit parentheses to document the intent (e.g.,
cfg!(test) || (check_path_executable("poetry").is_some() && poetry_lock_exists))
and ensure test_parse_poetry_scripts still creates poetry.lock if you keep the
lock requirement.
- Around line 73-82: The nested if-let expression used to compute tasks_table
inside the let-chain is hard to read; refactor by first resolving the tasks
table into a local variable (e.g., let tasks_table_opt =
poe_section.get("tasks").and_then(|inner| inner.as_table()).or_else(||
poe_section.as_table());) and then use a simple let-chain that checks
tasks_table_opt.is_some() and (cfg!(test) ||
check_path_executable("poe").is_some()) to bind tasks_table; update references
to use tasks_table (from tasks_table_opt.unwrap()/as_ref()) and keep symbols
poe_section, tasks_table, and check_path_executable unchanged.

---

Outside diff comments:
In `@Makefile`:
- Line 1: Update the .PHONY declaration to include the new lint target and other
non-file-producing targets to avoid accidental skipping: add lint plus format,
_builder, inspect_mcp, and test_long to the existing .PHONY list so targets like
build/tests/run/install/builder/publish remain correct and the lint recipe
always executes.

In `@src/mcp/server.rs`:
- Around line 2620-2625: The empty test test_concurrency_limit_in_task_start
should either be removed or implemented to actually exercise the task_start path
under a constrained JobManager; implement it by creating a server instance
configured with a low-concurrency JobManagerConfig (add/use a test helper like
new_with_allowlist_and_job_manager or new_with_job_manager_config) and drive
task_start RPCs until the JobManager returns the concurrency error, then assert
the expected error is returned; alternatively, delete the empty test if coverage
via test_concurrency_limit_enforcement is sufficient.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: df943a8c-44a7-4bc5-af46-a6ae853f12e8

📥 Commits

Reviewing files that changed from the base of the PR and between a85b255 and 6c61292.

📒 Files selected for processing (25)
  • .github/workflows/integration.yml
  • .github/workflows/rust.yml
  • Cargo.toml
  • Makefile
  • rust-toolchain.toml
  • src/allowlist.rs
  • src/commands/init.rs
  • src/commands/list.rs
  • src/commands/mcp.rs
  • src/mcp/allowlist.rs
  • src/mcp/dto.rs
  • src/mcp/server.rs
  • src/parsers/parse_cmake.rs
  • src/parsers/parse_github_actions.rs
  • src/parsers/parse_gradle.rs
  • src/parsers/parse_justfile.rs
  • src/parsers/parse_package_json.rs
  • src/parsers/parse_pom_xml.rs
  • src/parsers/parse_pyproject_toml.rs
  • src/parsers/parse_taskfile.rs
  • src/parsers/parse_travis_ci.rs
  • src/prompt.rs
  • src/task_discovery.rs
  • tests/Dockerfile.builder
  • tests/docker_unit/Dockerfile

Comment on lines +26 to 32
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Cache key should also key on the Rust toolchain version.

key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} does not include the rustc version. When you bump rust-toolchain.toml from 1.91.1 to a newer version in a future PR, the cached target/ will be reused even though it was built by an older rustc, which can cause confusing rebuild diagnostics or, worse, stale artifacts. Consider hashing rust-toolchain.toml (or the Cargo.toml rust-version) into the cache key.

Proposed change
-          key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
+          key: ${{ runner.os }}-cargo-${{ hashFiles('rust-toolchain.toml', '**/Cargo.lock') }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('rust-toolchain.toml', '**/Cargo.lock') }}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/rust.yml around lines 26 - 32, The cache key in the GitHub
Actions step (the `key` field currently set to `${{ runner.os }}-cargo-${{
hashFiles('**/Cargo.lock') }}`) does not include the Rust toolchain version and
can reuse artifacts built with a different rustc; update the `key` to also hash
the rust-toolchain file (or Cargo.toml rust-version) so toolchain bumps
invalidate the cache—specifically modify the `key` expression to include
`hashFiles('rust-toolchain.toml')` (or `hashFiles('**/Cargo.toml')` targeting
rust-version) alongside the existing Cargo.lock hash to ensure `target/` and
registry caches are version-aware.

Comment thread Cargo.toml
[package]
name = "dela"
version = "0.0.6"
rust-version = "1.91.1"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

🧩 Analysis chain

🌐 Web query:

Cargo rust-version field convention patch version MSRV

💡 Result:

The rust-version field in Cargo.toml specifies the Minimum Supported Rust Version (MSRV), the minimum Rust toolchain version supported by the package. It is an optional [package] key that Cargo uses to error on compilation if the toolchain is too old (unless --ignore-rust-version is used). Format: A bare semantic version number with at least one component (major, major.minor, or major.minor.patch), no semver operators or pre-release identifiers. Official docs use "1.56" (major.minor). Full patch like "1.56.1" is allowed but not shown in examples. Convention for patch version: Official Cargo documentation does not mandate including the patch level; major.minor is the standard and sufficient. Cargo treats rust-version with caret-like compatibility internally for resolution (e.g., "^package.rust-version" for deps), but the declared value is exact for checking the user's toolchain (>= declared version). Projects commonly use major.minor (e.g., stable-2 like 1.75 for current stable minus 2), updating minor when raising MSRV, often as a minor SemVer change in the crate (not breaking). Use tools like cargo-msrv to verify the actual MSRV matches the declared value. Support expectations: Dependencies should have at least one compatible version; Cargo resolver prefers compatible ones (with resolver.incompatible-rust-versions = "fallback" default in edition 2024). Policy recommendation: Define a support window (e.g., latest stable, or N-2 minors), test with cargo-msrv verify.

Citations:


Use rust-version = "1.91" (major.minor only, no patch version).

Including the patch version (1.91.1) causes cargo to reject users on Rust 1.91.0, even though the crate compiles fine on that version. The rust-version field checks toolchain >= declared version, so 1.91.0 < 1.91.1 incorrectly flags compliant users. The Cargo documentation convention and standard practice is major.minor only; the rust-toolchain.toml already pins the exact build version for contributors and CI.

Proposed change
-rust-version = "1.91.1"
+rust-version = "1.91"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
rust-version = "1.91.1"
rust-version = "1.91"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Cargo.toml` at line 4, Update the Cargo.toml rust-version field from the
patch-pinned value to a major.minor only value: change the rust-version
declaration (the "rust-version" entry in Cargo.toml) from "1.91.1" to "1.91" so
Cargo's version check accepts Rust 1.91.0 while your rust-toolchain.toml
continues to pin the exact build for CI/contributors.

Comment thread rust-toolchain.toml
Comment on lines +1 to +3
[toolchain]
channel = "1.91.1"
components = ["rustfmt", "clippy"]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Toolchain pin is fine; consider whether you want exact-patch or minor pinning.

channel = "1.91.1" will cause rustup to install/use exactly that patch version for every contributor, which is great for CI reproducibility but means contributors locally won't pick up patch fixes (e.g., a hypothetical 1.91.2) without a manifest bump. If you'd prefer to allow patch updates while still pinning the minor toolchain, channel = "1.91" is the common alternative. As-is is acceptable — just flagging the trade-off.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rust-toolchain.toml` around lines 1 - 3, The toolchain is pinned to an exact
patch via channel = "1.91.1"; decide whether you want exact-patch or minor
pinning and update the channel string accordingly (e.g., change channel =
"1.91.1" to channel = "1.91" to allow patch updates), and ensure components =
["rustfmt","clippy"] remains unchanged; update the channel value in the rust
toolchain manifest where the [toolchain] section and channel key are defined.

Comment thread src/parsers/parse_pyproject_toml.rs
Comment on lines +73 to +82
if let Some(poe) = toml.get("tool")
&& let Some(poe_section) = poe.get("poe")
{
// If there is a nested "tasks" key, use that table; otherwise, use the poe_section table directly
if let Some(tasks_table) = if let Some(inner) = poe_section.get("tasks") {
inner.as_table()
} else {
poe_section.as_table()
} && (cfg!(test) || check_path_executable("poe").is_some())
{
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Nested if let ... else as the bound expression of an outer let-chain is hard to read.

if let Some(tasks_table) = if let Some(inner) = poe_section.get("tasks") {
    inner.as_table()
} else {
    poe_section.as_table()
} && (cfg!(test) || check_path_executable("poe").is_some())

It parses, but the visual grouping of the inner if let { … } else { … } against the outer && is easy to misread. Extracting the table resolution removes the ambiguity and keeps the let-chain shallow.

♻️ Suggested refactor
-    if let Some(poe) = toml.get("tool")
-        && let Some(poe_section) = poe.get("poe")
-    {
-        // If there is a nested "tasks" key, use that table; otherwise, use the poe_section table directly
-        if let Some(tasks_table) = if let Some(inner) = poe_section.get("tasks") {
-            inner.as_table()
-        } else {
-            poe_section.as_table()
-        } && (cfg!(test) || check_path_executable("poe").is_some())
-        {
+    if let Some(poe) = toml.get("tool")
+        && let Some(poe_section) = poe.get("poe")
+    {
+        // If there is a nested "tasks" key, use that table; otherwise, use the poe_section table directly
+        let tasks_table = poe_section
+            .get("tasks")
+            .and_then(|inner| inner.as_table())
+            .or_else(|| poe_section.as_table());
+        if let Some(tasks_table) = tasks_table
+            && (cfg!(test) || check_path_executable("poe").is_some())
+        {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/parsers/parse_pyproject_toml.rs` around lines 73 - 82, The nested if-let
expression used to compute tasks_table inside the let-chain is hard to read;
refactor by first resolving the tasks table into a local variable (e.g., let
tasks_table_opt = poe_section.get("tasks").and_then(|inner|
inner.as_table()).or_else(|| poe_section.as_table());) and then use a simple
let-chain that checks tasks_table_opt.is_some() and (cfg!(test) ||
check_path_executable("poe").is_some()) to bind tasks_table; update references
to use tasks_table (from tasks_table_opt.unwrap()/as_ref()) and keep symbols
poe_section, tasks_table, and check_path_executable unchanged.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
src/parsers/parse_pyproject_toml.rs (1)

77-82: 🧹 Nitpick | 🔵 Trivial

Refactor nested bound expression in let-chain for readability.

This is still difficult to scan quickly. Consider resolving tasks_table first, then applying the executable guard in a separate let-chain.

♻️ Suggested refactor
-        if let Some(tasks_table) = if let Some(inner) = poe_section.get("tasks") {
-            inner.as_table()
-        } else {
-            poe_section.as_table()
-        } && (cfg!(test) || check_path_executable("poe").is_some())
+        let tasks_table = poe_section
+            .get("tasks")
+            .and_then(|inner| inner.as_table())
+            .or_else(|| poe_section.as_table());
+
+        if let Some(tasks_table) = tasks_table
+            && (cfg!(test) || check_path_executable("poe").is_some())
         {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/parsers/parse_pyproject_toml.rs` around lines 77 - 82, The nested
let-chain that computes tasks_table mixes extracting the table from poe_section
and an executable check (check_path_executable("poe")) making it hard to read;
refactor parse_pyproject_toml.rs by first binding tasks_table in its own let
(e.g., let tasks_table = if let Some(inner) = poe_section.get("tasks") {
inner.as_table() } else { poe_section.as_table() };), then use a separate if-let
/ let-chain to check tasks_table.is_some() && (cfg!(test) ||
check_path_executable("poe").is_some()) before entering the block — keep the
same symbols (poe_section, tasks_table, check_path_executable) and preserve
original behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/parsers/parse_pyproject_toml.rs`:
- Around line 77-82: The nested let-chain that computes tasks_table mixes
extracting the table from poe_section and an executable check
(check_path_executable("poe")) making it hard to read; refactor
parse_pyproject_toml.rs by first binding tasks_table in its own let (e.g., let
tasks_table = if let Some(inner) = poe_section.get("tasks") { inner.as_table() }
else { poe_section.as_table() };), then use a separate if-let / let-chain to
check tasks_table.is_some() && (cfg!(test) ||
check_path_executable("poe").is_some()) before entering the block — keep the
same symbols (poe_section, tasks_table, check_path_executable) and preserve
original behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e5a239c2-dec3-4514-9d79-230a97bd8e0b

📥 Commits

Reviewing files that changed from the base of the PR and between 6c61292 and cae14ed.

📒 Files selected for processing (1)
  • src/parsers/parse_pyproject_toml.rs

@aleyan aleyan merged commit b559473 into main Apr 29, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant