#!/usr/bin/env bash set -euo pipefail REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" MODE="fix" GIT_ONLY=false usage() { cat <<'EOF' Usage: scripts/format-code.sh [--check|--fix] [--git] Modes: --check Check formatting only --fix Apply formatting changes --git Limit scope to staged C++ files Defaults to formatting all C++ files in src/, include/, and tests/. Files under tests/fixtures/ are always excluded. EOF } while [[ $# -gt 0 ]]; do case "$1" in --check) MODE="check" shift ;; --fix) MODE="fix" shift ;; --git) GIT_ONLY=true shift ;; -h|--help) usage exit 0 ;; *) printf 'Unknown option: %s\n' "$1" >&2 usage >&2 exit 2 ;; esac done if [[ -n "${LLVM_BIN:-}" && -x "${LLVM_BIN}/clang-format" ]]; then CLANG_FORMAT="${LLVM_BIN}/clang-format" elif command -v clang-format >/dev/null 2>&1; then CLANG_FORMAT="$(command -v clang-format)" else printf 'clang-format not found in PATH or LLVM_BIN\n' >&2 exit 1 fi if [[ ! -f "$REPO_ROOT/.clang-format" ]]; then printf '.clang-format not found at repo root\n' >&2 exit 1 fi cd "$REPO_ROOT" declare -a files=() append_file_if_cpp() { local file="$1" [[ -f "$file" ]] || return 0 [[ "$file" == tests/fixtures/* ]] && return 0 case "$file" in src/*|include/*|tests/*) ;; *) return 0 ;; esac case "$file" in *.c|*.cc|*.cpp|*.cxx|*.h|*.hh|*.hpp) files+=("$file") ;; esac } if [[ "$GIT_ONLY" == true ]]; then while IFS= read -r file; do [[ -n "$file" ]] || continue append_file_if_cpp "$file" done < <(git diff --cached --name-only --diff-filter=ACMR) else while IFS= read -r file; do [[ -n "$file" ]] || continue files+=("$file") done < <( find src include tests \( -type f \) \ \( -name '*.c' -o -name '*.cc' -o -name '*.cpp' -o -name '*.cxx' -o -name '*.h' -o -name '*.hh' -o -name '*.hpp' \) \ -not -path 'tests/fixtures/*' \ | sort ) fi if [[ ${#files[@]} -eq 0 ]]; then printf 'No C++ files to process\n' exit 0 fi if [[ "$GIT_ONLY" == true ]]; then deduped=() while IFS= read -r line; do [[ -n "$line" ]] && deduped+=("$line") done < <(printf '%s\n' "${files[@]}" | sort -u) files=("${deduped[@]}") fi failures=0 for file in "${files[@]}"; do if [[ "$MODE" == "check" ]]; then if ! "$CLANG_FORMAT" --style=file --dry-run --Werror "$file"; then failures=$((failures + 1)) fi else "$CLANG_FORMAT" --style=file -i "$file" fi done if [[ "$MODE" == "check" ]]; then if [[ "$failures" -gt 0 ]]; then printf '%d file(s) need formatting\n' "$failures" >&2 exit 1 fi printf 'Formatting check passed\n' exit 0 fi printf 'Formatted %d file(s)\n' "${#files[@]}"