diff --git a/rust-toolchain/LICENSE b/rust-toolchain/LICENSE
new file mode 100644
index 0000000..31aa793
--- /dev/null
+++ b/rust-toolchain/LICENSE
@@ -0,0 +1,23 @@
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/rust-toolchain/README.md b/rust-toolchain/README.md
new file mode 100644
index 0000000..2ea5437
--- /dev/null
+++ b/rust-toolchain/README.md
@@ -0,0 +1,107 @@
+# Install Rust Toolchain
+
+This GitHub Action installs a Rust toolchain using rustup. It is designed for
+one-line concise usage and good defaults.
+
+
+
+## Example workflow
+
+```yaml
+name: test suite
+on: [push, pull_request]
+
+jobs:
+ test:
+ name: cargo test
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@stable
+ - run: cargo test --all-features
+```
+
+The selection of Rust toolchain is made based on the particular @rev of this
+Action being requested. For example "dtolnay/rust-toolchain@nightly" pulls in
+the nightly Rust toolchain, while "dtolnay/rust-toolchain@1.42.0" pulls in
+1.42.0.
+
+
+
+## Inputs
+
+All inputs are optional.
+
+
+
+ | Name |
+ Description |
+
+
+ toolchain |
+
+ Rustup toolchain specifier e.g. stable, nightly, 1.42.0, nightly-2022-01-01.
+ Important: the default is to match the @rev as described above.
+ When passing an explicit toolchain as an input instead of @rev, you'll want to use "dtolnay/rust-toolchain@master" as the revision of the action.
+ |
+
+
+ targets |
+ Comma-separated string of additional targets to install e.g. wasm32-unknown-unknown |
+
+
+ components |
+ Comma-separated string of additional components to install e.g. clippy, rustfmt |
+
+
+
+
+
+## Outputs
+
+
+
+ | Name |
+ Description |
+
+
+ cachekey |
+ A short hash of the installed rustc version, appropriate for use as a cache key. "20220627a831" |
+
+
+ name |
+ Rustup's name for the selected version of the toolchain, like "1.62.0". Suitable for use with cargo +${{steps.toolchain.outputs.name}}. |
+
+
+
+
+
+## Toolchain expressions
+
+The following forms are available for projects that use a sliding window of
+compiler support.
+
+```yaml
+ # Installs the most recent stable toolchain as of the specified time
+ # offset, which may be written in years, months, weeks, or days.
+ - uses: dtolnay/rust-toolchain@master
+ with:
+ toolchain: stable 18 months ago
+```
+
+```yaml
+ # Installs the stable toolchain which preceded the most recent one by
+ # the specified number of minor versions.
+ - uses: dtolnay/rust-toolchain@master
+ with:
+ toolchain: stable minus 8 releases
+```
+
+
+
+## License
+
+The scripts and documentation in this project are released under the [MIT
+License].
+
+[MIT License]: LICENSE
diff --git a/rust-toolchain/action.yml b/rust-toolchain/action.yml
new file mode 100644
index 0000000..2b57d9c
--- /dev/null
+++ b/rust-toolchain/action.yml
@@ -0,0 +1,149 @@
+name: rustup toolchain install
+author: David Tolnay
+description: Install the Rust toolchain
+branding:
+ icon: activity
+ color: purple
+
+inputs:
+ toolchain:
+ description: Rust toolchain specification -- see https://rust-lang.github.io/rustup/concepts/toolchains.html#toolchain-specification
+ required: true
+ targets:
+ description: Comma-separated list of target triples to install for this toolchain
+ required: false
+ target:
+ description: Alias for `targets`
+ required: false
+ components:
+ description: Comma-separated list of components to be additionally installed
+ required: false
+ rustup-version:
+ description: The version of rustup used during installation
+ required: false
+ default: 1.28.2
+
+outputs:
+ cachekey:
+ description: A short hash of the rustc version, appropriate for use as a cache key. "20220627a831"
+ value: ${{steps.rustc-version.outputs.cachekey}}
+ name:
+ description: Rustup's name for the selected version of the toolchain. "1.62.0" # suitable for use with `cargo +${{steps.toolchain.outputs.name}}`
+ value: ${{steps.parse.outputs.toolchain}}
+
+runs:
+ using: composite
+ steps:
+ - id: parse
+ run: |
+ : parse toolchain version
+ if [[ $toolchain =~ ^stable' '[0-9]+' '(year|month|week|day)s?' 'ago$ ]]; then
+ if [[ ${{runner.os}} == macOS ]]; then
+ echo "toolchain=1.$((($(date -v-$(sed 's/stable \([0-9]*\) \(.\).*/\1\2/' <<< $toolchain) +%s)/60/60/24-16569)/7/6))" >> $GITHUB_OUTPUT
+ else
+ echo "toolchain=1.$((($(date --date "${toolchain#stable }" +%s)/60/60/24-16569)/7/6))" >> $GITHUB_OUTPUT
+ fi
+ elif [[ $toolchain =~ ^stable' 'minus' '[0-9]+' 'releases?$ ]]; then
+ echo "toolchain=1.$((($(date +%s)/60/60/24-16569)/7/6-${toolchain//[^0-9]/}))" >> $GITHUB_OUTPUT
+ elif [[ $toolchain =~ ^1\.[0-9]+$ ]]; then
+ echo "toolchain=1.$((i=${toolchain#1.}, c=($(date +%s)/60/60/24-16569)/7/6, i+9*i*(10*i<=c)+90*i*(100*i<=c)))" >> $GITHUB_OUTPUT
+ else
+ echo "toolchain=$toolchain" >> $GITHUB_OUTPUT
+ fi
+ env:
+ toolchain: ${{inputs.toolchain}}
+ shell: bash
+
+ - id: flags
+ run: |
+ : construct rustup command line
+ echo "targets=$(for t in ${targets//,/ }; do echo -n ' --target' $t; done)" >> $GITHUB_OUTPUT
+ echo "components=$(for c in ${components//,/ }; do echo -n ' --component' $c; done)" >> $GITHUB_OUTPUT
+ echo "downgrade=${{steps.parse.outputs.toolchain == 'nightly' && inputs.components && ' --allow-downgrade' || ''}}" >> $GITHUB_OUTPUT
+ env:
+ targets: ${{inputs.targets || inputs.target || ''}}
+ components: ${{inputs.components}}
+ shell: bash
+
+ - run: |
+ : set $CARGO_HOME
+ echo CARGO_HOME=${CARGO_HOME:-"${{runner.os == 'Windows' && '$USERPROFILE\.cargo' || '$HOME/.cargo'}}"} >> $GITHUB_ENV
+ shell: bash
+
+ - run: |
+ : install rustup if needed
+ if ! command -v rustup &>/dev/null; then
+ curl --proto '=https' --tlsv1.2 --retry 10 --retry-connrefused --location --silent --show-error --fail https://sh.rustup.rs | RUSTUP_VERSION="$RUSTUP_VERSION" sh -s -- --default-toolchain none -y
+ echo "$CARGO_HOME/bin" >> $GITHUB_PATH
+ fi
+ if: runner.os != 'Windows'
+ env:
+ RUSTUP_VERSION: ${{ inputs.rustup-version }}
+ shell: bash
+
+ - run: |
+ : install rustup if needed on windows
+ if ! command -v rustup &>/dev/null; then
+ curl --proto '=https' --tlsv1.2 --retry 10 --retry-connrefused --location --silent --show-error --fail https://win.rustup.rs/${{runner.arch == 'ARM64' && 'aarch64' || 'x86_64'}} --output '${{runner.temp}}\rustup-init.exe'
+ RUSTUP_VERSION="$RUSTUP_VERSION" '${{runner.temp}}\rustup-init.exe' --default-toolchain none --no-modify-path -y
+ echo "$CARGO_HOME\bin" >> $GITHUB_PATH
+ fi
+ if: runner.os == 'Windows'
+ env:
+ RUSTUP_VERSION: ${{ inputs.rustup-version }}
+ shell: bash
+
+ - name: rustup toolchain install ${{steps.parse.outputs.toolchain}}
+ run: rustup toolchain install ${{steps.parse.outputs.toolchain}}${{steps.flags.outputs.targets}}${{steps.flags.outputs.components}} --profile minimal${{steps.flags.outputs.downgrade}} --no-self-update
+ shell: bash
+
+ - run: rustup default ${{steps.parse.outputs.toolchain}}
+ shell: bash
+ continue-on-error: true # https://github.com/dtolnay/rust-toolchain/issues/127
+
+ - id: rustc-version
+ run: |
+ : create cachekey
+ DATE=$(rustc +${{steps.parse.outputs.toolchain}} --version --verbose | sed -ne 's/^commit-date: \(20[0-9][0-9]\)-\([01][0-9]\)-\([0-3][0-9]\)$/\1\2\3/p')
+ HASH=$(rustc +${{steps.parse.outputs.toolchain}} --version --verbose | sed -ne 's/^commit-hash: //p')
+ echo "cachekey=$(echo $DATE$HASH | head -c12)" >> $GITHUB_OUTPUT
+ shell: bash
+
+ - run: |
+ : disable incremental compilation
+ if [ -z "${CARGO_INCREMENTAL+set}" ]; then
+ echo CARGO_INCREMENTAL=0 >> $GITHUB_ENV
+ fi
+ shell: bash
+
+ - run: |
+ : enable colors in Cargo output
+ if [ -z "${CARGO_TERM_COLOR+set}" ]; then
+ echo CARGO_TERM_COLOR=always >> $GITHUB_ENV
+ fi
+ shell: bash
+
+ - run: |
+ : enable Cargo sparse registry
+ # implemented in 1.66, stabilized in 1.68, made default in 1.70
+ if [ -z "${CARGO_REGISTRIES_CRATES_IO_PROTOCOL+set}" -o -f "${{runner.temp}}"/.implicit_cargo_registries_crates_io_protocol ]; then
+ if rustc +${{steps.parse.outputs.toolchain}} --version --verbose | grep -q '^release: 1\.6[89]\.'; then
+ touch "${{runner.temp}}"/.implicit_cargo_registries_crates_io_protocol || true
+ echo CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse >> $GITHUB_ENV
+ elif rustc +${{steps.parse.outputs.toolchain}} --version --verbose | grep -q '^release: 1\.6[67]\.'; then
+ touch "${{runner.temp}}"/.implicit_cargo_registries_crates_io_protocol || true
+ echo CARGO_REGISTRIES_CRATES_IO_PROTOCOL=git >> $GITHUB_ENV
+ fi
+ fi
+ shell: bash
+
+ - run: |
+ : work around spurious network errors in curl 8.0
+ # https://rust-lang.zulipchat.com/#narrow/stream/246057-t-cargo/topic/timeout.20investigation
+ if rustc +${{steps.parse.outputs.toolchain}} --version --verbose | grep -q '^release: 1\.7[01]\.'; then
+ echo CARGO_HTTP_MULTIPLEXING=false >> $GITHUB_ENV
+ fi
+ shell: bash
+
+ - run: rustc +${{steps.parse.outputs.toolchain}} --version --verbose
+ shell: bash
diff --git a/rust-toolchain/scripts/update-revs.sh b/rust-toolchain/scripts/update-revs.sh
new file mode 100755
index 0000000..697d135
--- /dev/null
+++ b/rust-toolchain/scripts/update-revs.sh
@@ -0,0 +1,62 @@
+#!/bin/bash
+
+cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.."
+
+if ! git diff-index --quiet HEAD; then
+ echo "Not running update-revs.sh because git working directory is dirty" >&2
+ exit 1
+fi
+
+patch_releases=(
+ 1.12.1 1.15.1 1.22.1 1.24.1 1.26.1 1.26.2 1.27.1 1.27.2 1.29.1 1.29.2 1.30.1
+ 1.31.1 1.34.1 1.34.2 1.41.1 1.43.1 1.44.1 1.45.1 1.45.2 1.52.1 1.56.1 1.58.1
+ 1.62.1 1.66.1 1.67.1 1.68.1 1.68.2 1.71.1 1.72.1 1.74.1 1.77.1 1.77.2 1.80.1
+ 1.84.1 1.85.1
+)
+
+releases() {
+ printf "%s\n" 1.{0..100}.0 ${patch_releases[@]} | sort -V
+}
+
+base=$(git rev-parse HEAD)
+push=()
+
+declare -A minor
+for rev in `releases`; do
+ minor[${rev%.*}]=$rev
+done
+
+for rev in `releases` stable beta nightly; do
+ echo "Updating $rev branch"
+ git checkout --quiet "$base"
+ git branch --quiet --delete --force $rev &>/dev/null || true
+ if [[ $rev == 1* ]]; then
+ sed -i "/^ toolchain:/,+2d; s/\${{inputs\.toolchain}}/$rev/" action.yml
+ else
+ sed -i "s/required: true/required: false\n default: $rev/" action.yml
+ fi
+ git add action.yml
+ git commit --quiet --message "toolchain: $rev"
+ git checkout --quiet -b $rev
+ push+=("$rev:refs/heads/$rev")
+ if [ "${minor[${rev%.*}]}" == $rev ]; then
+ push+=("$rev:refs/heads/${rev%.*}")
+ fi
+done
+
+for tool in clippy miri; do
+ echo "Updating $tool branch"
+ git checkout --quiet --detach nightly
+ git branch --quiet --delete --force $tool &>/dev/null || true
+ default=$tool
+ if [ $tool == miri ]; then default+=,\ rust-src; fi
+ sed -i "/required: false/{N;s/\n$/\n default: $default\n/}" action.yml
+ git add action.yml
+ git commit --quiet --message "components: $tool"
+ git checkout --quiet -b $tool
+ push+=("$tool:refs/heads/$tool")
+done
+
+git checkout --quiet "$base"
+
+echo "git push origin --force-with-lease ${push[@]}"