name: Publish crate

on:
  push:
    branches:
      - main
    paths:
      - ".github/workflows/cargo-publish.yml"
      - "Cargo.lock"
      - "Cargo.toml"
      - "LICENSE"
      - "README.md"
      - "src/**"
  workflow_dispatch:

permissions:
  contents: read

concurrency:
  group: cargo-publish-${{ github.ref }}
  cancel-in-progress: false

jobs:
  publish:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Install Rust
        uses: dtolnay/rust-toolchain@stable

      - name: Read crate metadata
        id: crate
        shell: bash
        run: |
          python3 - <<'PY' >> "$GITHUB_OUTPUT"
          import json
          import subprocess

          metadata = json.loads(
              subprocess.check_output(
                  ["cargo", "metadata", "--no-deps", "--format-version", "1"],
                  text=True,
              )
          )
          package = metadata["packages"][0]
          print(f"name={package['name']}")
          print(f"version={package['version']}")
          PY

      - name: Check formatting
        run: cargo fmt --all -- --check

      - name: Run clippy
        run: cargo clippy --all-targets -- -D warnings

      - name: Run tests
        run: cargo test

      - name: Package crate
        run: cargo package

      - name: Check crates.io version
        id: published
        shell: bash
        run: |
          crate="${{ steps.crate.outputs.name }}"
          version="${{ steps.crate.outputs.version }}"
          index_file="${RUNNER_TEMP:-/tmp}/crates-index.jsonl"

          case "${#crate}" in
            1)
              index_path="1/$crate"
              ;;
            2)
              index_path="2/$crate"
              ;;
            3)
              index_path="3/${crate:0:1}/$crate"
              ;;
            *)
              index_path="${crate:0:2}/${crate:2:2}/$crate"
              ;;
          esac

          status="$(
            curl \
              --silent \
              --show-error \
              --location \
              --retry 3 \
              --user-agent "token-check-release (https://github.com/hanlife02/token-check)" \
              --output "$index_file" \
              --write-out "%{http_code}" \
              "https://index.crates.io/$index_path"
          )"

          case "$status" in
            200)
              if python3 - "$version" "$index_file" <<'PY'
          import json
          import sys

          version = sys.argv[1]
          index_file = sys.argv[2]

          with open(index_file, encoding="utf-8") as lines:
              for line in lines:
                  if line.strip() and json.loads(line).get("vers") == version:
                      sys.exit(0)

          sys.exit(1)
          PY
              then
                echo "exists=true" >> "$GITHUB_OUTPUT"
              else
                echo "exists=false" >> "$GITHUB_OUTPUT"
              fi
              ;;
            404)
              echo "exists=false" >> "$GITHUB_OUTPUT"
              ;;
            *)
              cat "$index_file"
              echo "Unexpected crates.io status: $status" >&2
              exit 1
              ;;
          esac

      - name: Publish to crates.io
        if: steps.published.outputs.exists == 'false'
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
        run: |
          if [ -z "${CARGO_REGISTRY_TOKEN:-}" ]; then
            echo "CARGO_REGISTRY_TOKEN is not configured." >&2
            exit 1
          fi

          cargo publish

      - name: Skip already-published version
        if: steps.published.outputs.exists == 'true'
        run: |
          echo "${{ steps.crate.outputs.name }} ${{ steps.crate.outputs.version }} is already published."
