name: 'Install Rust toolchain'
description: 'Install a rust toolchain and cache the crates index'

inputs:
  toolchain:
    description: 'Default toolchan to install'
    required: false
    default: 'default'
  lockfiles:
    description: 'Path glob for Cargo.lock files to use as cache keys'
    required: false
    default: '**/Cargo.lock'

runs:
  using: composite
  steps:
    - name: Install Rust
      shell: bash
      id: select
      run: |
        # Determine MSRV as N in `1.N.0` by looking at the `rust-version`
        # located in the root `Cargo.toml`.
        msrv=$(grep 'rust-version.*1' Cargo.toml | sed 's/.*\.\([0-9]*\)\..*/\1/')

        if [ "${{ inputs.toolchain }}" = "default" ]; then
          echo "version=1.$((msrv+2)).0" >> "$GITHUB_OUTPUT"
        elif [ "${{ inputs.toolchain }}" = "msrv" ]; then
          echo "version=1.$msrv.0" >> "$GITHUB_OUTPUT"
        else
          echo "version=${{ inputs.toolchain }}" >> "$GITHUB_OUTPUT"
        fi

    - name: Install Rust
      shell: bash
      run: |
        rustup set profile minimal
        rustup update "${{ steps.select.outputs.version }}" --no-self-update
        rustup default "${{ steps.select.outputs.version }}"

        # Save disk space by avoiding incremental compilation. Also turn down
        # debuginfo from 2 to 0 to help save disk space.
        cat >> "$GITHUB_ENV" <<EOF
        CARGO_INCREMENTAL=0
        CARGO_PROFILE_DEV_DEBUG=0
        CARGO_PROFILE_TEST_DEBUG=0
        EOF

        # Deny warnings on CI to keep our code warning-free as it lands in-tree.
        echo RUSTFLAGS="-D warnings" >> "$GITHUB_ENV"

        if [[ "${{ runner.os }}" = "macOS" ]]; then
          cat >> "$GITHUB_ENV" <<EOF
        CARGO_PROFILE_DEV_SPLIT_DEBUGINFO=unpacked
        CARGO_PROFILE_TEST_SPLIT_DEBUGINFO=unpacked
        EOF
        fi

        # Use a more efficient method for fetching the crates.io-index than
        # the (currently) default git-based index.
        cat >> "$GITHUB_ENV" <<EOF
        CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse
        EOF

    - name: Require semicolons in WIT
      shell: bash
      run: echo WIT_REQUIRE_SEMICOLONS=1 >> "$GITHUB_ENV"

    - name: Install the WASI target
      shell: bash
      run: rustup target add wasm32-wasi wasm32-unknown-unknown

    - name: Choose registry cache key
      shell: bash
      # Update the registry index cache at most once per day. actions/cache
      # won't write changes back to the cache if the cache key already exists,
      # so this means every job may have to re-download the index entries which
      # are new since the last time the cache key changed. Changing the cache
      # key relatively frequently keeps the amount of duplicated work down. But
      # changing it too frequently means we might hit the 10GB quota too
      # quickly, which would cause GitHub to evict other caches we still want.
      run: echo CARGO_REGISTRY_CACHE_KEY=$(date +%Y%m%d) >> $GITHUB_ENV

    - name: Cache Cargo registry index
      uses: actions/cache@v3
      with:
        path: ~/.cargo/registry/index/
        key: cargo-registry-${{ env.CARGO_REGISTRY_CACHE_KEY }}
        # Any older registry-index cache is still valid. It's a git clone, so
        # cargo only has to pull down the changes since the index was cached.
        restore-keys: cargo-registry-

    - name: Cache crate sources for dependencies
      uses: actions/cache@v3
      with:
        path: |
          ~/.cargo/registry/cache/
          ~/.cargo/git/db/
        key: cargo-crates-${{ inputs.lockfiles }}-${{ hashFiles(inputs.lockfiles) }}
        # If Cargo.lock has changed, we probably will need to get the source
        # code for some crates we don't already have. But any crates we have
        # source cached for are still valid. The only problem is nothing
        # removes old crate sources from the cache, so using `restore-keys`
        # this way may use more of our GitHub cache quota than we'd like.
        #
        # Also, scope this cache by which Cargo.lock we're building from.
        # Otherwise, whichever job writes the cache first will get its
        # dependencies cached, and that cache will be used as the basis for the
        # next job, even though odds are pretty good the cache is useless.
        restore-keys: cargo-crates-${{ inputs.lockfiles }}-

# TODO: on cache miss, after cargo has updated the registry index, run `git gc`
