name: Fuzz

on:
  pull_request:
    branches: [main]
  schedule:
    # Longer campaign weekly (Sunday 4 AM UTC)
    - cron: '0 4 * * 0'

permissions:
  contents: read

env:
  CARGO_TERM_COLOR: always

jobs:
  fuzz:
    name: Fuzz ${{ matrix.target }}
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        target:
          - fuzz_xmlrpc_parse
          - fuzz_url_parser
          - fuzz_flag_parser
          - fuzz_tls_der
    steps:
      - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10  # v6
        with:
          persist-credentials: false

      # This MUST pin dtolnay/rust-toolchain's `master` branch, NOT a
      # channel branch. Only `master`'s action.yml declares an explicit
      # `toolchain` input; the channel branches (`stable`, `1.89.0`, ...)
      # hardcode their version and silently ignore `toolchain:`, installing
      # stable instead of the nightly pinned below. Dependabot bumps the
      # SHA-pinned action across every workflow at once, so do NOT let it
      # collapse this onto the stable-channel SHA that ci.yml/release.yml
      # use -- that swap is what broke this job (it installed 1.89.0 and
      # `cargo +nightly fuzz run` then had no nightly toolchain).
      #
      # nightly-2026-05-02 is the last nightly that compiles cargo-fuzz's
      # transitive `rustix v0.36.5` dep. Newer nightlies dropped the
      # unstable `rustc_layout_scalar_valid_range_{start,end}` attribute
      # form rustix 0.36.5 uses. Bump this once cargo-fuzz pulls a rustix
      # release that compiles on current nightly.
      - uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9  # master
        with:
          toolchain: nightly-2026-05-02

      - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4  # v2
        with:
          key: fuzz-${{ matrix.target }}

      - name: Install cargo-fuzz
        run: cargo install cargo-fuzz --locked

      - name: Run fuzzer
        run: |
          if [ "${{ github.event_name }}" = "schedule" ]; then
            MAX_TIME=300   # 5 minutes per target on schedule
          else
            MAX_TIME=30    # 30 seconds per target on PR
          fi
          cargo +nightly fuzz run ${{ matrix.target }} -- -max_total_time=$MAX_TIME
