#!/usr/bin/env bash
# Reject Cargo.lock changes that aren't accompanied by a Cargo.toml change.
# Lockfile drift should originate from cargo, not hand edits — see AGENTS.md.
#
# Accepts a Cargo.toml change anywhere in the workspace (root or any
# crate under `crates/`), since cargo-driven lock updates legitimately
# follow from per-crate dependency edits.
set -euo pipefail

staged_changed() {
  git diff --cached --name-only --diff-filter=ACMR | grep -Fx "$1" >/dev/null
}

staged_changed_any() {
  # Match any path ending in the given basename; e.g. `Cargo.toml`
  # matches both the root manifest and `crates/*/Cargo.toml`.
  #
  # Escape regex metacharacters in the basename before splicing into
  # `grep -E` — without this, `Cargo.toml`'s `.` would match any
  # character, so a (hypothetical) staged `Cargo-toml` would falsely
  # satisfy the gate.
  local basename="$1"
  local escaped="${basename//./\\.}"
  git diff --cached --name-only --diff-filter=ACMR \
    | grep -E "(^|/)${escaped}$" >/dev/null
}

if staged_changed Cargo.lock && ! staged_changed_any Cargo.toml; then
  cat >&2 <<'EOF'
error: Cargo.lock is staged without a corresponding Cargo.toml change.

Lockfile drift must come from cargo, not hand edits. Either:
  - stage the matching Cargo.toml change (root or per-crate), or
  - unstage Cargo.lock (`git restore --staged Cargo.lock`) and rerun cargo.
EOF
  exit 1
fi
