alef 0.84.2

Opinionated polyglot binding generator for Rust libraries
Documentation
PASS=0
FAIL=0

# Per-test assertion failure counter.
#
# ~keep `run_test` cannot rely on `set -e` to stop a test function at its first
# failed assertion: invoking the function as the condition of an `if` (or either
# side of `&&`/`||`) disables errexit for the whole dynamic extent of that call.
# A failing assertion's `return 1` therefore does not abort the function, and the
# function's exit status ends up being just its *last* command's — so every
# assertion but the last could fail while the test still reported PASS. Each
# assertion helper records its failure here instead, and `run_test` consults the
# counter alongside the exit status. Adding `set -e` would not fix this; errexit
# is already set and is ignored in exactly this context.
ASSERT_FAILURES=0

# Report one failed assertion and record it. Returns 1 so the calling helper
# still propagates a non-zero status to callers that do check it.
__fail_assertion() {
  echo "$1" >&2
  ASSERT_FAILURES=$((ASSERT_FAILURES + 1))
  return 1
}

assert_equals() {
  local actual="$1" expected="$2" label="$3"
  if [ "$actual" != "$expected" ]; then
    __fail_assertion "FAIL [$label]: expected '$expected', got '$actual'"
  fi
}

assert_contains() {
  local actual="$1" expected="$2" label="$3"
  if [[ "$actual" != *"$expected"* ]]; then
    __fail_assertion "FAIL [$label]: expected to contain '$expected'"
  fi
}

assert_not_empty() {
  local actual="$1" label="$2"
  # `jq -r` renders JSON null and empty containers as the literal text "null", "[]" and "{}",
  # so a bare emptiness test would pass on them. A legitimate 0 or false still renders as text and passes.
  if [ -z "$actual" ] || [ "$actual" = "null" ] || [ "$actual" = "[]" ] || [ "$actual" = "{}" ]; then
    __fail_assertion "FAIL [$label]: expected non-empty value"
  fi
}

assert_count_min() {
  local count="$1" min="$2" label="$3"
  if [ "$count" -lt "$min" ]; then
    __fail_assertion "FAIL [$label]: expected at least $min elements, got $count"
  fi
}

assert_greater_than() {
  local val="$1" threshold="$2" label="$3"
  if [ "$(echo "$val > $threshold" | bc -l)" != "1" ]; then
    __fail_assertion "FAIL [$label]: expected $val > $threshold"
  fi
}

assert_greater_than_or_equal() {
  local actual="$1" expected="$2" label="$3"
  if [ "$actual" -lt "$expected" ]; then
    __fail_assertion "FAIL [$label]: expected $actual >= $expected"
  fi
}

assert_is_empty() {
  local actual="$1" label="$2"
  if [ -n "$actual" ]; then
    __fail_assertion "FAIL [$label]: expected empty value, got '$actual'"
  fi
}

assert_less_than() {
  local actual="$1" expected="$2" label="$3"
  if [ "$actual" -ge "$expected" ]; then
    __fail_assertion "FAIL [$label]: expected $actual < $expected"
  fi
}

assert_less_than_or_equal() {
  local actual="$1" expected="$2" label="$3"
  if [ "$actual" -gt "$expected" ]; then
    __fail_assertion "FAIL [$label]: expected $actual <= $expected"
  fi
}

assert_not_contains() {
  local actual="$1" expected="$2" label="$3"
  if [[ "$actual" == *"$expected"* ]]; then
    __fail_assertion "FAIL [$label]: expected not to contain '$expected'"
  fi
}

# Run one generated test function and record its outcome.
#
# ~keep The test runs to completion (errexit is inert here, see ASSERT_FAILURES
# above) so that every assertion is reported, not just the first. The verdict is
# the disjunction of the function's own exit status and the assertion counter:
# either alone is insufficient — the status misses non-final failed assertions,
# and the counter misses a test that bailed out through a bare `return 1`.
run_test() {
  local name="$1"
  ASSERT_FAILURES=0
  local status=0
  "$name" || status=$?
  if [ "$status" -eq 0 ] && [ "$ASSERT_FAILURES" -eq 0 ]; then
    echo "PASS: $name"
    PASS=$((PASS + 1))
  else
    echo "FAIL: $name"
    FAIL=$((FAIL + 1))
  fi
}