#!/usr/bin/env bash

# Every QML component in a module directory must be listed in that directory's
# qmldir.
#
# An unregistered component is not a quiet problem. Quickshell fails the whole
# configuration with "X is not a type" -- so the settings window dies, and with
# it the bar, the dock and the rest of the shell. It has happened twice: once
# adding ContainersPage, once adding PickerRow and SettingsTabs. Both times the
# file was written, the live shell hot-reloaded, and the desktop went down while
# somebody was using it.
#
# The failure is trivial to prevent and expensive to discover, which is exactly
# what a contract is for. This one is pure file inspection: no shell is started,
# so it can be run before the change ever reaches the running desktop.

set -uo pipefail

repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
shell_dir="$repo_dir/config/dot/quickshell"

fail() {
    printf 'qmldir registration contract: %s\n' "$1" >&2
    exit 1
}

[[ -d "$shell_dir" ]] || fail "missing $shell_dir"

checked=0
missing=()

while IFS= read -r qmldir; do
    directory="$(dirname "$qmldir")"
    for component in "$directory"/*.qml; do
        [[ -e "$component" ]] || continue
        name="$(basename "$component" .qml)"
        checked=$((checked + 1))
        # A singleton declares itself with `singleton NAME`; everything else is
        # `NAME VERSION FILE`. Either form counts as registered.
        grep -qE "^(singleton +)?${name}( |$)" "$qmldir" \
            || missing+=("${directory#"$repo_dir"/}/$name.qml")
    done
done < <(find "$shell_dir" -name qmldir)

if (( ${#missing[@]} > 0 )); then
    printf 'qmldir registration contract: %d component(s) are not registered:\n' "${#missing[@]}" >&2
    printf '  %s\n' "${missing[@]}" >&2
    printf 'Quickshell fails the entire configuration on these, taking the shell down with it.\n' >&2
    exit 1
fi

(( checked > 0 )) || fail 'no components were checked, so this proves nothing'

printf 'qmldir registration contract: ok (%d components registered)\n' "$checked"
