# interpres.
#
# Everything below the variable block is the standard recipe set, identical in
# every repository; project values live in the variable block only.
binary := "interpres-decode"
package := "./cmd/interpres-decode"

# What the test and bench recipes sweep. Scope this to the logic packages when a thin
# cmd/ drags the coverage floor down, for example "./internal/... ./pkg/...". Never name
# a directory the project does not have: a pattern that matches nothing is a setup
# failure, not an empty run.
packages := "./..."

bindir := env_var_or_default("BINDIR", env_var("HOME") / ".local" / "bin")

default:
    @just --list

# Compile. Zero errors, zero warnings.
build:
    CGO_ENABLED=0 go build -ldflags "-s -w" -o bin/{{binary}} {{package}}

# The test gate: the suite, no cache, the coverage floor.
test:
    #!/usr/bin/env perl
    system(q{go}, q{test}, q{-count=1}, q{-timeout}, q{30m},
      q{-coverprofile}, q{coverage.out}, qw({{packages}})) == 0
      or die qq{the test suite failed\n};
    open(my $c, q{-|}, q{go}, q{tool}, q{cover}, q{-func=coverage.out}) or die qq{cover: $!};
    my $total;
    while (my $l = <$c>) { $total = $1 if $l =~ m{^total:\s+\S+\s+([0-9.]+)%} }
    close($c);
    die qq{no total line in coverage.out\n} unless defined $total;
    printf qq{Total coverage: %s%%\n}, $total;
    exit($total < 80 ? 1 : 0);

# The same suite under the race detector. The expensive one.
race:
    go test -race -count=1 -timeout 30m {{packages}}

# Fast scoped run for iterating. This is the one that runs after every edit.
unit pkgs=packages run=".*":
    go test {{pkgs}} -run '{{run}}'

# Time-boxed fuzz of one target in one package. The package is required; never a gate.
fuzz target pkg fuzztime="60s":
    go test -run '^$' -fuzz '{{target}}' -fuzztime={{fuzztime}} {{pkg}}

# Benchmarks. On an idle machine only.
bench pkgs=packages:
    go test -run '^$' -bench=. -benchmem -count=5 {{pkgs}}

# Format in place.
fmt:
    gofmt -w .

# Zero diff. Prints nothing when everything is formatted.
fmt-check:
    #!/usr/bin/env perl
    open(my $g, q{-|}, q{gofmt}, q{-l}, q{.}) or die qq{gofmt: $!};
    my @bad = <$g>;
    close($g);
    print @bad;
    exit(@bad ? 1 : 0);

# Both static gates: go vet and go fix -diff.
vet:
    go vet ./...
    go fix -diff ./...

# The definition of done, in one command. Once per task, never per edit.
gates: build fmt-check vet test race

# Build artefacts only, not the installed binary.
clean:
    rm -rf bin/ coverage.out

# Build, then copy the binary into bindir.
install: build
    install -d "{{bindir}}"
    install -m 755 bin/{{binary}} "{{bindir}}/{{binary}}"

# Remove the installed binary.
uninstall:
    rm -f "{{bindir}}/{{binary}}"

# Run the program. The flag is there because `go run` does not stamp the build otherwise.
run:
    go run -buildvcs=true {{package}}

# Run with watch or hot reload, where the project has one.
dev:
    go run -buildvcs=true {{package}}

# Runs the official toml-test compliance suite in both directions, decoder and encoder, against the built adapter; toml-test must be on PATH (go install github.com/toml-lang/toml-test/v2/cmd/toml-test@v2.2.0); not standard because no canonical recipe covers a domain compliance suite.
toml-test: build
    toml-test test -decoder=bin/interpres-decode -encoder='bin/interpres-decode --encode' -toml=1.1

# Coverage report as an HTML map from the gate's profile; not standard because the gate needs only the numeric floor, and a browser artefact is exploration, not a gate.
coverage-html: test
    go tool cover -html=coverage.out -o coverage.html

# Cross-compile smoke: the library and the command build for the foreign architectures and the browser and edge runtimes; not a gate, it is a hand-run convenience and runs std-lib only.
cross:
    GOARCH=arm64 go build ./...
    GOARCH=loong64 go build ./...
    GOARCH=riscv64 go build ./...
    GOOS=js GOARCH=wasm go build ./...
    GOOS=wasip1 GOARCH=wasm go build ./...
    GOARCH=arm64 CGO_ENABLED=0 go build -o /dev/null {{package}}
    GOARCH=loong64 CGO_ENABLED=0 go build -o /dev/null {{package}}
    GOARCH=riscv64 CGO_ENABLED=0 go build -o /dev/null {{package}}

# Runs the example program under examples/basic; not standard because `run` runs the adapter, and an example is documentation, not the product.
example:
    go run ./examples/basic

# The release pre-flight, in one command: the branch, a clean tree, a sync with origin, the gates, and a CHANGELOG section ready to release. Not a gate, it is the checklist before a release may even be discussed.
release-check version:
    #!/usr/bin/env perl
    # The version arrives through the recipe interpolation: just does not hand
    # positional arguments to a shebang script's @ARGV.
    my $version = q{ {{version}} };
    $version =~ m{\Av?\d+\.\d+\.\d+\z} or die qq{usage: just release-check X.Y.Z\n};
    my $branch = qx{git rev-parse --abbrev-ref HEAD};
    chomp $branch;
    $branch eq q{development} or die qq{release-check: on '$branch', cut releases from development\n};
    my $dirty = qx{git status --porcelain};
    $dirty eq q{} or die qq{release-check: the working tree is dirty\n};
    system(qw{git fetch origin}) == 0 or die qq{release-check: git fetch failed\n};
    my $local = qx{git rev-parse development};
    my $remote = qx{git rev-parse origin/development};
    $local eq $remote or die qq{release-check: development is out of sync with origin\n};
    my $changelog = do { open(my $fh, q{<}, q{CHANGELOG.md}) or die qq{release-check: cannot read CHANGELOG.md: $!\n}; local $/; <$fh> };
    $changelog =~ m{## \[development\]\n\n### \w+} or die qq{release-check: the [development] section of CHANGELOG.md is missing or empty\n};
    print qq{branch, tree, sync and changelog verified; running the gates\n};
    system(qw{just gates}) == 0 or die qq{release-check: the gates failed\n};
    print qq{release-check: ready to release $version\n};
    print qq{after tagging, verify the /v2 module resolves through the proxy:\n};
    print qq{  cd \$(mktemp -d) && go mod init t && GOPRIVATE= GOPROXY=https://proxy.golang.org go get sourcedock.dev/petrbalvin/interpres/v2\@$version\n};

# Compares the toml-test counts the documentation names with the live suite run; not standard, it exists because a corpus change used to be corrected by hand.
docs-drift:
    perl scripts/docs-drift.pl
