From dab5f91926c9c8668334c6a2f69d9dc9e3d90677 Mon Sep 17 00:00:00 2001
From: David Tolnay <dtolnay@gmail.com>
Date: Sun, 25 Sep 2022 05:29:24 -0700
Subject: [PATCH] Add toolchain expressions like '18 months ago'

---
 .github/workflows/ci.yml | 16 ++++++++++++++--
 README.md                | 29 +++++++++++++++++++++++++++++
 action.yml               | 30 ++++++++++++++++++++++++------
 3 files changed, 67 insertions(+), 8 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index a023d05..379ce63 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -18,7 +18,7 @@ jobs:
       matrix:
         name: [Linux]
         os: [ubuntu]
-        rust: [nightly, beta, stable, 1.62.0, 1.0.0]
+        rust: [nightly, beta, stable, 1.62.0, 1.0.0, 18 months ago, stable minus 8 releases]
         include:
           - name: macOS
             os: macos
@@ -26,16 +26,28 @@ jobs:
           - name: macOS
             os: macos
             rust: 1.62.0
+          - name: macOS
+            os: macos
+            rust: 18 months ago
+          - name: macOS
+            os: macos
+            rust: stable minus 8 releases
           - name: Windows
             os: windows
             rust: nightly
           - name: Windows
             os: windows
             rust: 1.62.0
+          - name: Windows
+            os: windows
+            rust: 18 months ago
+          - name: Windows
+            os: windows
+            rust: stable minus 8 releases
     steps:
       - uses: actions/checkout@v3
       - uses: ./
-        name: Run dtolnay/rust-toolchain@${{matrix.rust}}
+        name: Run dtolnay/rust-toolchain${{contains(matrix.rust, ' ') && ' for ' || '@'}}${{matrix.rust}}
         id: toolchain
         with:
           toolchain: ${{matrix.rust}}
diff --git a/README.md b/README.md
index 329509c..a04a49b 100644
--- a/README.md
+++ b/README.md
@@ -3,6 +3,8 @@
 This GitHub Action installs a Rust toolchain using rustup. It is designed for
 one-line concise usage and good defaults.
 
+<br>
+
 ## Example workflow
 
 ```yaml
@@ -24,6 +26,8 @@ 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.
 
+<br>
+
 ## Inputs
 
 All inputs are optional.
@@ -51,6 +55,31 @@ All inputs are optional.
 </tr>
 </table>
 
+<br>
+
+## 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: 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
+```
+
+<br>
+
 ## License
 
 The scripts and documentation in this project are released under the [MIT
diff --git a/action.yml b/action.yml
index 8feb110..cc7f9cc 100644
--- a/action.yml
+++ b/action.yml
@@ -27,6 +27,24 @@ outputs:
 runs:
   using: composite
   steps:
+    - id: parse
+      run: |
+        : parse toolchain version
+        if [[ $toolchain =~ ^[0-9]+' '(year|month|week|day)s?' 'ago$ ]]; then
+          if [[ ${{runner.os}} == macOS ]]; then
+            echo "::set-output name=toolchain::1.$((($(date -v-$(sed 's/\([0-9]*\) \(.\).*/\1\2/' <<< $toolchain) +%s)/60/60/24-16569)/7/6))"
+          else
+            echo "::set-output name=toolchain::1.$((($(date --date "$toolchain" +%s)/60/60/24-16569)/7/6))"
+          fi
+        elif [[ $toolchain =~ ^stable' 'minus' '[0-9]+' 'releases?$ ]]; then
+          echo "::set-output name=toolchain::1.$((($(date +%s)/60/60/24-16569)/7/6-${toolchain//[^0-9]/}))"
+        else
+          echo "::set-output name=toolchain::$toolchain"
+        fi
+      env:
+        toolchain: ${{inputs.toolchain}}
+      shell: bash
+
     - id: flags
       run: |
         : construct rustup command line
@@ -47,18 +65,18 @@ runs:
       if: runner.os != 'Windows'
       shell: bash
 
-    - name: rustup toolchain install ${{inputs.toolchain}}
-      run: rustup toolchain install ${{inputs.toolchain}}${{steps.flags.outputs.targets}}${{steps.flags.outputs.components}} --profile minimal${{steps.flags.outputs.downgrade}} --no-self-update
+    - 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 ${{inputs.toolchain}}
+    - run: rustup default ${{steps.parse.outputs.toolchain}}
       shell: bash
 
     - id: rustc-version
       run: |
         : create cachekey
-        DATE=$(rustc +${{inputs.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 +${{inputs.toolchain}} --version --verbose | sed -ne 's/^commit-hash: //p')
+        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 "::set-output name=cachekey::$(echo $DATE$HASH | head -c12)"
       shell: bash
 
@@ -67,5 +85,5 @@ runs:
         echo CARGO_INCREMENTAL=0 >> $GITHUB_ENV
       shell: bash
 
-    - run: rustc +${{inputs.toolchain}} --version --verbose
+    - run: rustc +${{steps.parse.outputs.toolchain}} --version --verbose
       shell: bash