Skip to content

feat(merge-toml): support inline array indexing in paths#112

Merged
yukimemi merged 1 commit into
mainfrom
feat/merge-toml-inline-array-index
May 20, 2026
Merged

feat(merge-toml): support inline array indexing in paths#112
yukimemi merged 1 commit into
mainfrom
feat/merge-toml-inline-array-index

Conversation

@yukimemi
Copy link
Copy Markdown
Owner

@yukimemi yukimemi commented May 19, 2026

Summary

Closes #111. Extends the name[idx] syntax shipped in #108 to inline arrays (Item::Value(Value::Array(_))) in addition to ArrayOfTables. So a template with paths = ["tasks.on-add.dependencies[0]"] can own a single element of a cargo-make-style dependencies = ["fmt", "clippy"] list while the consumer freely appends more at idx 1+.

Motivating example

# template (Makefile.toml)
[tasks.on-add]
dependencies = ["cargo make fmt-check"]

# manifest
paths = ["tasks.on-add.dependencies[0]"]   # kata owns the first dep
                                           # consumer can append more

Implementation

  • item_at_table_path adds an Item::Value(Value::Array(_)) branch alongside Item::ArrayOfTables. Tries AoT first (the kata#107 shape), falls through to inline array. Inline-array leaves return Item::Value(elem.clone()); a continuing path after an inline-array index bails out (the elements are scalars / inline values that don't carry sub-paths).
  • set_in_table at a leaf KeyIndex now dispatches on the incoming Item variantItem::Tableensure_aot_element, Item::Value → new ensure_array_element. A shape mismatch (existing is AoT but incoming is inline, or vice versa) naturally no-ops because each helper checks the existing slot's actual type.
  • New ensure_array_element helper mirrors ensure_aot_element's rules:
    • key missing AND idx != 0 → no-op
    • key missing AND idx == 0 → bootstrap with Value::Array(vec![placeholder]), return slot 0
    • key present, not an inline array → no-op (refuse-to-clobber)
    • key present, empty array AND idx == 0 → push placeholder, return slot 0
    • key present, idx < len → return slot idx
    • any other out-of-range → no-op
  • collect_dotted_paths emits prefix[N] for inline-array entries so regex specs like //^tags\[0\]$// can target them. Inline-array elements aren't recursed into — parse_segments doesn't accept [N][M] and there's no Key step into a scalar.

Scope notes

Test plan

  • cargo test --lib modes::merge_toml — 27 tests pass (9 new: idx-0 replacement, bootstrap, out-of-range skip, refuse-to-clobber non-array, scalar element replacement, idempotency, shape-mismatch AoT-vs-inline refuse, regex-targets-element, collect_dotted_paths white-box).
  • cargo fmt --all -- --check
  • cargo make clippy
  • cargo make lock-check
  • CI on Linux/macOS/Windows (runs on push).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Inline TOML arrays can now be merged using index-based path syntax (e.g., dependencies[0]).
    • Regex patterns can now target specific inline array elements through dotted path enumeration.
    • Conservative bootstrap behavior ensures safe array creation and updates during merge operations.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 19, 2026

Warning

Rate limit exceeded

@yukimemi has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 29 minutes and 51 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 286846d6-e41d-42bc-85df-5c944ccb2e7f

📥 Commits

Reviewing files that changed from the base of the PR and between 451c040 and 2b70101.

📒 Files selected for processing (1)
  • src/modes/merge_toml.rs
📝 Walkthrough

Walkthrough

This PR extends TOML merge functionality to support inline-array element access using name[idx] indexing. It adds path resolution and mutation logic for inline arrays, path enumeration for discovery, and tests covering bootstrapping, shape validation, and idempotency.

Changes

TOML inline-array indexing

Layer / File(s) Summary
Documentation and imports
src/modes/merge_toml.rs
Mode documentation specifies how name[idx] indexing behaves for inline arrays: bootstrap only at index 0 when missing, refuse shape mismatches, and dispatch based on incoming template item variant. Import Array for element handling.
Inline-array element mutation and dispatch
src/modes/merge_toml.rs
New ensure_array_element helper conservatively creates or extends inline arrays, returning mutable access to slots. set_in_table leaf logic dispatches Item::Value updates through the helper and Item::Table updates through array-of-tables logic.
Inline-array element resolution
src/modes/merge_toml.rs
item_at_table_path for KeyIndex segments first tries array-of-tables resolution, then falls back to inline-array lookup; returns leaf values for complete paths, rejects shape mismatches, and refuses to walk under inline-array elements.
Path discovery for inline arrays
src/modes/merge_toml.rs
collect_dotted_paths enumerates indexed element paths (e.g., dependencies[0]) for Value::Array entries, enabling regex patterns to target specific inline-array elements.
Inline-array indexing test suite
src/modes/merge_toml.rs
Tests cover element replacement, bootstrapping missing arrays, out-of-range no-ops, non-array slot rejection, idempotency, shape-mismatch refusal between AoT and inline arrays, regex element targeting, and path enumeration.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

  • yukimemi/kata#108: Both PRs extend merge-toml path parsing for name[idx] via PathSeg and KeyIndex handling, but this PR generalizes KeyIndex resolution to inline arrays in addition to array-of-tables.
  • yukimemi/kata#90: This PR extends collect_dotted_paths and path-walk logic to enumerate and target inline-array indexed paths, complementing the regex path sweeping introduced in that PR.
  • yukimemi/kata#12: Both PRs extend core path-walk and setter implementation in src/modes/merge_toml.rs; this PR adds KeyIndex resolution and mutation for inline arrays, building on earlier path-handling infrastructure.

Poem

🐰 Inline arrays march in rows,
Indexed paths where logic flows,
deps[0] now finds its place,
Bootstrap grace, no clobber's case! 🎯
Merge and match with TOML grace~

🚥 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 and concisely describes the main feature added: support for inline array indexing in TOML merge paths, which is the primary focus of the changeset.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/merge-toml-inline-array-index

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

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

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces support for indexing and merging inline arrays in TOML files, enabling path-based access and updates to specific array elements (e.g., name[idx]). Key changes include updating path collection to emit element-level paths, modifying the path traversal and setter logic to handle inline arrays alongside existing Array of Tables (AoT) support, and adding a suite of unit tests. Feedback focuses on optimizing the ensure_array_element helper function by utilizing the entry API to eliminate redundant lookups.

Comment thread src/modes/merge_toml.rs Outdated
Comment on lines +537 to +558
if !table.contains_key(key) {
if idx != 0 {
return None;
}
let mut arr = Array::new();
arr.push(Value::from(0i64));
table.insert(key, Item::Value(Value::Array(arr)));
return table.get_mut(key)?.as_array_mut()?.get_mut(0);
}
let entry = table.get_mut(key)?;
let arr = entry.as_array_mut()?;
if arr.is_empty() {
if idx != 0 {
return None;
}
arr.push(Value::from(0i64));
return arr.get_mut(0);
}
if idx >= arr.len() {
return None;
}
arr.get_mut(idx)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The ensure_array_element function performs multiple redundant lookups on the table (e.g., contains_key, insert, and get_mut). This can be optimized and made more idiomatic by using the entry API, which performs a single lookup for both checking existence and retrieving or inserting the entry.

    match table.entry(key) {
        toml_edit::Entry::Vacant(v) => {
            if idx != 0 {
                return None;
            }
            let mut arr = Array::new();
            arr.push(Value::from(0i64));
            v.insert(Item::Value(Value::Array(arr)))
                .as_value_mut()?
                .as_array_mut()?
                .get_mut(0)
        }
        toml_edit::Entry::Occupied(mut o) => {
            let arr = o.get_mut().as_array_mut()?;
            if arr.is_empty() {
                if idx != 0 {
                    return None;
                }
                arr.push(Value::from(0i64));
                return arr.get_mut(0);
            }
            if idx >= arr.len() {
                return None;
            }
            arr.get_mut(idx)
        }
    }

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

@gemini-code-assist Done in 2b70101. Switched ensure_array_element to the toml_edit::Entry API and applied the same refactor to ensure_aot_element for symmetry — both helpers now do a single lookup per call. One wrinkle worth noting for next time: OccupiedEntry::get_mut borrows from the entry, so the resulting reference can't escape the match arm (returning it triggers E0515). slot.into_mut() consumes the entry and yields &'a mut Item with the table's lifetime, which is what the helper needs.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

That's a great catch regarding the OccupiedEntry::get_mut lifetime constraints. Using into_mut() is indeed the correct, idiomatic way to consume the entry and obtain a mutable reference with the appropriate lifetime for the table. Your refactor looks solid and consistent across both helpers.

Closes #111. Extends the `name[idx]` syntax shipped in #108 to
inline arrays (`Item::Value(Value::Array(_))`) in addition to the
`ArrayOfTables` shape that issue covered. So a template with
`paths = ["tasks.on-add.dependencies[0]"]` can own one element of
a cargo-make-style `dependencies = ["fmt", "clippy"]` list while
the consumer freely appends their own steps at idx 1+.

Implementation:
- `item_at_table_path` adds an `Item::Value(Value::Array(_))`
  branch alongside the existing `Item::ArrayOfTables` one. Tries
  AoT first (the kata#107 shape) and falls through to inline
  array. Inline-array leaves return `Item::Value(elem.clone())`;
  a continuing path after an inline-array index bails out (the
  elements are scalars / inline values that don't carry sub-
  paths).
- `set_in_table` at a leaf `KeyIndex` now dispatches on the
  incoming `Item` variant — `Item::Table` → `ensure_aot_element`,
  `Item::Value` → new `ensure_array_element`. A shape mismatch
  (existing is AoT but incoming is inline, or vice versa)
  naturally no-ops inside the helper because each one checks the
  existing slot's actual type.
- New `ensure_array_element` helper mirrors `ensure_aot_element`:
  bootstrap only at `idx == 0` (missing entry or empty array),
  no-op on out-of-range and on non-array existing slots. Both
  helpers use the `toml_edit::Entry` API (single lookup per
  call) instead of the older `contains_key`/`get_mut` split —
  Gemini #112 review.
- `collect_dotted_paths` emits `prefix[N]` for inline-array
  entries so regex specs like `//^tags\[0\]$//` can target them.
  Inline-array elements aren't recursed into (their values are
  scalars / inline tables; `parse_segments` doesn't accept
  `[N][M]`).

Tests: 9 new `modes::merge_toml::tests` mirroring the #107
coverage for inline arrays — bootstrap, idx-0 replacement,
out-of-range skip, refuse-to-clobber non-array, scalar element
replacement, idempotency, shape mismatch (AoT existing / inline
incoming) refuse, regex-targets-element, and
`collect_dotted_paths` white-box. Total `merge_toml` tests:
27 passing.
@yukimemi yukimemi force-pushed the feat/merge-toml-inline-array-index branch from 451c040 to 2b70101 Compare May 19, 2026 23:42
@yukimemi yukimemi merged commit 2dedccf into main May 20, 2026
13 checks passed
@yukimemi yukimemi mentioned this pull request May 20, 2026
3 tasks
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.

merge-toml: name[idx] addressing for inline arrays (Value::Array), not just ArrayOfTables

1 participant