name: CI

on:
  pull_request:
  push:
    branches: [master]

jobs:
  fmt:
    name: Check formatting
    runs-on: ubuntu-latest
    steps:
    - uses: taiki-e/checkout-action@v1
    - uses: dtolnay/rust-toolchain@nightly
      with:
        components: rustfmt
    - name: Check Formatting
      run: cargo fmt -- --check

  taplo:
    name: Taplo
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: taiki-e/checkout-action@v1
      - name: Install Taplo
        uses: taiki-e/install-action@v2
        with:
          tool: taplo-cli
      - name: Run Taplo
        run: taplo fmt --check

  typos:
    name: Check for typos
    runs-on: ubuntu-latest
    timeout-minutes: 30
    steps:
      - uses: taiki-e/checkout-action@v1
      - uses: taiki-e/install-action@v2
        with:
          tool: typos-cli
      - name: run typos
        run: typos
      - name: Typos info
        if: failure()
        run: |
          echo 'To fix typos, please run `typos -w`'
          echo 'To check for a diff, run `typos`'
          echo 'You can find typos here: https://crates.io/crates/typos'

  tests:
    name: Test ${{ matrix.toolchain }} ${{ matrix.platform.name }}
    runs-on: ${{ matrix.platform.os }}

    strategy:
      fail-fast: false
      matrix:
        toolchain: [stable, nightly, '1.86']
        platform:
          # Note: Make sure that we test all the `docs.rs` targets defined in Cargo.toml!
          - { name: 'Windows 64bit MSVC', target: x86_64-pc-windows-msvc,   os: windows-latest,  }
          - { name: 'Windows 32bit MSVC', target: i686-pc-windows-msvc,     os: windows-latest,  }
          - { name: 'Windows 64bit GNU',  target: x86_64-pc-windows-gnu,    os: windows-latest, host: -x86_64-pc-windows-gnu }
          - { name: 'Windows 32bit GNU',  target: i686-pc-windows-gnu,      os: windows-latest, host: -i686-pc-windows-gnu }
          - { name: 'Linux 32bit',        target: i686-unknown-linux-gnu,   os: ubuntu-latest,   }
          - { name: 'Linux 64bit',        target: x86_64-unknown-linux-gnu, os: ubuntu-latest,   }
          - { name: 'X11',                target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: '--no-default-features --features=x11' }
          - { name: 'Wayland',            target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: '--no-default-features --features=wayland,wayland-dlopen' }
          - { name: 'Android',            target: aarch64-linux-android,    os: ubuntu-latest, options: '--package winit --features=android-native-activity', cmd: 'apk -- ' }
          - { name: 'Redox OS',           target: x86_64-unknown-redox,     os: ubuntu-latest,   }
          - { name: 'macOS x86_64',       target: x86_64-apple-darwin,      os: macos-latest,    }
          - { name: 'macOS Aarch64',      target: aarch64-apple-darwin,     os: macos-latest,    }
          - { name: 'iOS x86_64',         target: x86_64-apple-ios,         os: macos-latest,    }
          - { name: 'iOS Aarch64',        target: aarch64-apple-ios,        os: macos-latest,    }
          - { name: 'Web',                target: wasm32-unknown-unknown,   os: ubuntu-latest,   }
        exclude:
              # Web on nightly needs extra arguments

          - toolchain: nightly
            platform: { name: 'Web' }
            # Rustup is broken.
          - toolchain: nightly
            platform: { name: 'Windows 32bit GNU' }
          # Android is tested on stable-3
          - toolchain: '1.86'
            platform: { name: 'Android' }
          # Redox OS doesn't follow MSRV
          - toolchain: '1.86'
            platform: { name: 'Redox OS' }
        include:
          - toolchain: '1.86'
            platform: { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package winit --features=android-native-activity', cmd: 'apk -- ' }
          - toolchain: 'nightly'
            platform: { name: 'Web', target: wasm32-unknown-unknown, os: ubuntu-latest, test-options: -Zdoctest-xcompile }
          - toolchain: 'nightly'
            platform: {
              name:         'Web Atomic',
              target:       wasm32-unknown-unknown,
              os:           ubuntu-latest,
              options:      '-Zbuild-std=panic_abort,std',
              test-options: -Zdoctest-xcompile,
              rustflags:    '-Ctarget-feature=+atomics,+bulk-memory',
              components:   rust-src,
            }

    env:
      # Set more verbose terminal output
      CARGO_TERM_VERBOSE: true
      RUST_BACKTRACE: 1

      # Faster compilation and error on warnings
      RUSTFLAGS: '--codegen=debuginfo=0 --deny=warnings ${{ matrix.platform.rustflags }}'
      RUSTDOCFLAGS: ${{ matrix.platform.rustflags }}

      OPTIONS: --target=${{ matrix.platform.target }} ${{ matrix.platform.options }}
      TEST_OPTIONS: ${{ matrix.platform.test-options }}
      CMD: ${{ matrix.platform.cmd }}

    steps:
    - uses: taiki-e/checkout-action@v1

    - name: Restore cache of cargo folder
      # We use `restore` and later `save`, so that we can create the key after
      # the cache has been downloaded.
      #
      # This could be avoided if we added Cargo.lock to the repository.
      uses: actions/cache/restore@v5
      with:
        # https://doc.rust-lang.org/cargo/guide/cargo-home.html#caching-the-cargo-home-in-ci
        path: |
          ~/.cargo/registry/index/
          ~/.cargo/registry/cache/
          ~/.cargo/git/db/
        key: cargo-${{ matrix.toolchain }}-${{ matrix.platform.name }}-never-intended-to-be-found
        restore-keys: cargo-${{ matrix.toolchain }}-${{ matrix.platform.name }}

    - name: Generate lockfile
      # Also updates the crates.io index
      run: cargo generate-lockfile

    - name: Install GCC Multilib
      if: (matrix.platform.os == 'ubuntu-latest') && contains(matrix.platform.target, 'i686')
      run: sudo apt-get update && sudo apt-get install gcc-multilib

    - name: Cache cargo-apk
      if: contains(matrix.platform.target, 'android')
      id: cargo-apk-cache
      uses: actions/cache@v5
      with:
        path: ~/.cargo/bin/cargo-apk
        # Change this key if we update the required cargo-apk version
        key: cargo-apk-v0-9-7

    - uses: dtolnay/rust-toolchain@master
      if: contains(matrix.platform.target, 'android') && (steps.cargo-apk-cache.outputs.cache-hit != 'true')
      with:
        toolchain: stable

    - name: Install cargo-apk
      if: contains(matrix.platform.target, 'android') && (steps.cargo-apk-cache.outputs.cache-hit != 'true')
      run: cargo install cargo-apk --version=^0.9.7 --locked

    - uses: taiki-e/cache-cargo-install-action@v3
      if: contains(matrix.platform.target, 'wasm32') && matrix.toolchain == 'nightly'
      with:
        tool: wasm-bindgen-cli

    - uses: dtolnay/rust-toolchain@master
      with:
        toolchain: ${{ matrix.toolchain }}${{ matrix.platform.host }}
        targets: ${{ matrix.platform.target }}
        components: clippy, ${{ matrix.platform.components }}

    - name: Check documentation
      run: cargo doc --no-deps $OPTIONS --document-private-items
      env:
        RUSTDOCFLAGS: '--deny=warnings ${{ matrix.platform.rustflags }}'

    - name: Build crate
      run: cargo $CMD build $OPTIONS

    - name: Test winit core
      run: cargo test -p winit-core

    - name: Test winit Android
      if: contains(matrix.platform.target, 'android')
      run: cargo $CMD test -p winit-android --features native-activity --no-run

    - name: Test winit Common (EventHandler)
      run: cargo $CMD test -p winit-common --features event-handler --no-run

    - name: Test winit Common (CF)
      if: contains(matrix.platform.target, 'apple')
      run: cargo $CMD test -p winit-common --features core-foundation --no-run

    - name: Test winit Common (XKB)
      if: contains(matrix.platform.target, 'linux-gnu')
      run: cargo $CMD test -p winit-common --features xkb,x11,wayland --no-run

    - name: Test winit AppKit
      if: contains(matrix.platform.target, 'macos')
      run: cargo $CMD test -p winit-appkit $OPTIONS

    - name: Test winit Orbital
      if: contains(matrix.platform.target, 'redox')
      run: cargo test -p winit-orbital

    - name: Test winit UIKit
      if: contains(matrix.platform.target, 'ios')
      # TODO: Run on Simulator
      run: cargo $CMD test -p winit-uikit $OPTIONS --no-run

    - name: Test winit Web
      if: contains(matrix.platform.target, 'wasm')
      run: cargo $CMD test -p winit-web $OPTIONS --no-run

    - name: Test winit Win32
      if: contains(matrix.platform.target, 'windows')
      run: cargo $CMD test -p winit-win32 $OPTIONS

    - name: Test winit X11
      if: contains(matrix.platform.target, 'linux-gnu')
      run: cargo $CMD test -p winit-x11 --target=${{ matrix.platform.target }}

    - name: Test winit Wayland
      if: contains(matrix.platform.target, 'linux-gnu')
      run: cargo $CMD test -p winit-wayland --target=${{ matrix.platform.target }}

    # Test only on Linux x86_64, so we avoid spending unnecessary CI hours.
    - name: Test dpi crate
      if: >
        contains(matrix.platform.name, 'Linux 64bit') &&
        matrix.toolchain != '1.86'
      run: cargo test -p dpi

    - name: Check dpi crate (no_std)
      if: >
        contains(matrix.platform.name, 'Linux 64bit') &&
        matrix.toolchain != '1.86'
      run: cargo check -p dpi --no-default-features

    - name: Build tests
      if: >
        !contains(matrix.platform.target, 'redox') &&
        matrix.toolchain != '1.86'
      run: cargo $CMD test --no-run $OPTIONS

    - name: Run tests
      if: >
        !contains(matrix.platform.target, 'android') &&
        !contains(matrix.platform.target, 'ios') &&
        (!contains(matrix.platform.target, 'wasm32') || matrix.toolchain == 'nightly') &&
        !contains(matrix.platform.target, 'redox') &&
        matrix.toolchain != '1.86'
      run: cargo $CMD test $OPTIONS

    - name: Lint with clippy
      if: (matrix.toolchain == 'stable') && !contains(matrix.platform.options, '--no-default-features')
      run: cargo clippy --all-targets $OPTIONS $TEST_OPTIONS -- -Dwarnings

    - name: Build tests with serde enabled
      if: >
        !contains(matrix.platform.target, 'redox') &&
        matrix.toolchain != '1.86'
      run: cargo $CMD test --no-run $OPTIONS $TEST_OPTIONS --features serde

    - name: Run tests with serde enabled
      if: >
        !contains(matrix.platform.target, 'android') &&
        !contains(matrix.platform.target, 'ios') &&
        (!contains(matrix.platform.target, 'wasm32') || matrix.toolchain == 'nightly') &&
        !contains(matrix.platform.target, 'redox') &&
        matrix.toolchain != '1.86'
      run: cargo $CMD test $OPTIONS $TEST_OPTIONS --features serde

    - name: Check docs.rs documentation
      if: matrix.toolchain == 'nightly'
      run: cargo doc --no-deps $OPTIONS --features=serde,mint,android-native-activity
      env:
        RUSTDOCFLAGS: '--deny=warnings ${{ matrix.platform.rustflags }} --cfg=docsrs --cfg=unreleased_changelogs'

    # See restore step above
    - name: Save cache of cargo folder
      uses: actions/cache/save@v5
      with:
        path: |
          ~/.cargo/registry/index/
          ~/.cargo/registry/cache/
          ~/.cargo/git/db/
        key: cargo-${{ matrix.toolchain }}-${{ matrix.platform.name }}-${{ hashFiles('Cargo.lock') }}

  cargo-deny:
    name: Run cargo-deny
    runs-on: ubuntu-latest

    steps:
    - uses: taiki-e/checkout-action@v1
    - uses: EmbarkStudios/cargo-deny-action@v2
      with:
        log-level: error

  eslint:
    name: ESLint

    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: ./winit-web/src/script

    steps:
      - uses: taiki-e/checkout-action@v1
      - name: Setup NPM
        run: npm install
      - name: Run ESLint
        run: npx eslint@9.38.0

  swc:
    name: Minimize JavaScript

    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: ./winit-web/src/script

    steps:
      - uses: taiki-e/checkout-action@v1
      - name: Install SWC
        run: sudo npm i -g @swc/cli
      - name: Run SWC
        run: |
          swc . --ignore node_modules,**/*.d.ts --only **/*.ts -d . --out-file-extension min.js
      - name: Check for diff
        run: |
          [[ -z $(git status -s) ]]
