From b9761f6dee4293c25d23687ff062e9c32d00355c Mon Sep 17 00:00:00 2001
From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com>
Date: Tue, 8 Oct 2024 08:35:02 +0100
Subject: [PATCH] Add InterfaceMock attribute (#17)
---
.github/workflows/dotnet.yaml | 45 +++++++++++++++++++
.../Attributes.fs | 15 +++++++
.../README.md | 6 +++
.../SurfaceBaseline.txt | 5 +++
...pet.Plugin.InterfaceMock.Attributes.fsproj | 30 +++++++++++++
.../version.json | 11 +++++
...ippet.Plugin.InterfaceMock.Consumer.fsproj | 2 +-
.../README.md | 5 +++
.../TestSurface.fs | 26 +++++++++++
...e.Whippet.Plugin.InterfaceMock.Test.fsproj | 15 ++++---
WoofWare.Whippet.sln | 6 +++
11 files changed, 159 insertions(+), 7 deletions(-)
create mode 100644 Plugins/InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock.Attributes/Attributes.fs
create mode 100644 Plugins/InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock.Attributes/README.md
create mode 100644 Plugins/InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock.Attributes/SurfaceBaseline.txt
create mode 100644 Plugins/InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock.Attributes/WoofWare.Whippet.Plugin.InterfaceMock.Attributes.fsproj
create mode 100644 Plugins/InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock.Attributes/version.json
create mode 100644 Plugins/InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock.Test/TestSurface.fs
diff --git a/.github/workflows/dotnet.yaml b/.github/workflows/dotnet.yaml
index c3f5e65..dbd1cf4 100644
--- a/.github/workflows/dotnet.yaml
+++ b/.github/workflows/dotnet.yaml
@@ -190,6 +190,11 @@ jobs:
with:
name: nuget-package-httpclient
path: Plugins/HttpClient/WoofWare.Whippet.Plugin.HttpClient/bin/Release/WoofWare.Whippet.Plugin.HttpClient.*.nupkg
+ - name: Upload NuGet artifact (interfacemock attrs)
+ uses: actions/upload-artifact@v4
+ with:
+ name: nuget-package-interfacemock-attrs
+ path: Plugins/InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock.Attributes/bin/Release/WoofWare.Whippet.Plugin.InterfaceMock.Attributes.*.nupkg
- name: Upload NuGet artifact (interfacemock plugin)
uses: actions/upload-artifact@v4
with:
@@ -272,6 +277,14 @@ jobs:
- name: Check NuGet contents
# Verify that there is exactly one nupkg in the artifact that would be NuGet published
run: if [[ $(find packed-httpclient -maxdepth 1 -name 'WoofWare.Whippet.Plugin.HttpClient.*.nupkg' -printf c | wc -c) -ne "1" ]]; then exit 1; fi
+ - name: Download NuGet artifact (interfacemock attrs)
+ uses: actions/download-artifact@v4
+ with:
+ name: nuget-package-interfacemock-attrs
+ path: packed-interfacemock-attrs
+ - name: Check NuGet contents
+ # Verify that there is exactly one nupkg in the artifact that would be NuGet published
+ run: if [[ $(find packed-interfacemock-attrs -maxdepth 1 -name 'WoofWare.Whippet.Plugin.InterfaceMock.Attributes.*.nupkg' -printf c | wc -c) -ne "1" ]]; then exit 1; fi
- name: Download NuGet artifact (interfacemock plugin)
uses: actions/download-artifact@v4
with:
@@ -612,3 +625,35 @@ jobs:
nupkg-dir: packed/
dotnet: ${{ steps.dotnet-identify.outputs.dotnet }}
+ nuget-publish-interfacemock-attrs:
+ runs-on: ubuntu-latest
+ if: ${{ !github.event.repository.fork && github.ref == 'refs/heads/main' }}
+ needs: [all-required-checks-complete]
+ environment: main-deploy
+ permissions:
+ id-token: write
+ attestations: write
+ contents: read
+ steps:
+ - uses: actions/checkout@v4
+ - name: Install Nix
+ uses: cachix/install-nix-action@v29
+ with:
+ extra_nix_config: |
+ access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
+ - name: Download NuGet artifact
+ uses: actions/download-artifact@v4
+ with:
+ name: nuget-package-interfacemock-attrs
+ path: packed
+ - name: Identify `dotnet`
+ id: dotnet-identify
+ run: nix develop --command bash -c 'echo "dotnet=$(which dotnet)" >> $GITHUB_OUTPUT'
+ - name: Publish to NuGet
+ id: publish-success
+ uses: G-Research/common-actions/publish-nuget@2b7dc49cb14f3344fbe6019c14a31165e258c059
+ with:
+ package-name: WoofWare.Whippet.Plugin.InterfaceMock.Attributes
+ nuget-key: ${{ secrets.NUGET_API_KEY }}
+ nupkg-dir: packed/
+ dotnet: ${{ steps.dotnet-identify.outputs.dotnet }}
diff --git a/Plugins/InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock.Attributes/Attributes.fs b/Plugins/InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock.Attributes/Attributes.fs
new file mode 100644
index 0000000..bd02b6a
--- /dev/null
+++ b/Plugins/InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock.Attributes/Attributes.fs
@@ -0,0 +1,15 @@
+namespace WoofWare.Whippet.Plugin.InterfaceMock
+
+/// Attribute indicating an interface type for which the "Interface Mock" Whippet
+/// generator should apply during build.
+/// This generator creates a record which implements the interface,
+/// but where each method is represented as a record field, so you can use
+/// record update syntax to easily specify partially-implemented mock objects.
+/// You may optionally specify `isInternal = false` to get a mock with the public visibility modifier.
+type InterfaceMockAttribute (isInternal : bool) =
+ inherit System.Attribute ()
+ /// The default value of `isInternal`, the optional argument to the InterfaceMockAttribute constructor.
+ static member DefaultIsInternal = true
+
+ /// Shorthand for the "isExtensionMethod = false" constructor; see documentation there for details.
+ new () = InterfaceMockAttribute InterfaceMockAttribute.DefaultIsInternal
diff --git a/Plugins/InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock.Attributes/README.md b/Plugins/InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock.Attributes/README.md
new file mode 100644
index 0000000..6125d03
--- /dev/null
+++ b/Plugins/InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock.Attributes/README.md
@@ -0,0 +1,6 @@
+# WoofWare.Whippet.Plugin.InterfaceMock.Attributes
+
+This is a very slim runtime dependency which consumers of WoofWare.Whippet.Plugin.InterfaceMock may optionally take.
+This dependency contains attributes which control that source generator,
+although you may instead omit this dependency and control the generator entirely through configuration in consumer's `.fsproj`.
+Please see WoofWare.Whippet.Plugin.InterfaceMock's README for further information.
diff --git a/Plugins/InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock.Attributes/SurfaceBaseline.txt b/Plugins/InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock.Attributes/SurfaceBaseline.txt
new file mode 100644
index 0000000..c699ef4
--- /dev/null
+++ b/Plugins/InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock.Attributes/SurfaceBaseline.txt
@@ -0,0 +1,5 @@
+WoofWare.Whippet.Plugin.InterfaceMock.InterfaceMockAttribute inherit System.Attribute
+WoofWare.Whippet.Plugin.InterfaceMock.InterfaceMockAttribute..ctor [constructor]: bool
+WoofWare.Whippet.Plugin.InterfaceMock.InterfaceMockAttribute..ctor [constructor]: unit
+WoofWare.Whippet.Plugin.InterfaceMock.InterfaceMockAttribute.DefaultIsInternal [static property]: [read-only] bool
+WoofWare.Whippet.Plugin.InterfaceMock.InterfaceMockAttribute.get_DefaultIsInternal [static method]: unit -> bool
\ No newline at end of file
diff --git a/Plugins/InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock.Attributes/WoofWare.Whippet.Plugin.InterfaceMock.Attributes.fsproj b/Plugins/InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock.Attributes/WoofWare.Whippet.Plugin.InterfaceMock.Attributes.fsproj
new file mode 100644
index 0000000..33e106d
--- /dev/null
+++ b/Plugins/InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock.Attributes/WoofWare.Whippet.Plugin.InterfaceMock.Attributes.fsproj
@@ -0,0 +1,30 @@
+
+
+
+ netstandard2.1
+ true
+ Patrick Stevens
+ Copyright (c) Patrick Stevens 2024
+ Attributes to accompany the WoofWare.Whippet.Plugin.InterfaceMock source generator, to indicate what you want your types to be doing.
+ git
+ https://github.com/Smaug123/WoofWare.Whippet
+ MIT
+ README.md
+ fsharp;source-generator;source-gen;whippet;mock
+ true
+ FS3559
+ WoofWare.Whippet.Plugin.InterfaceMock.Attributes
+
+
+
+
+
+
+
+ True
+ /
+ README.md
+
+
+
+
diff --git a/Plugins/InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock.Attributes/version.json b/Plugins/InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock.Attributes/version.json
new file mode 100644
index 0000000..790e212
--- /dev/null
+++ b/Plugins/InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock.Attributes/version.json
@@ -0,0 +1,11 @@
+{
+ "version": "0.1",
+ "publicReleaseRefSpec": [
+ "^refs/heads/main$"
+ ],
+ "pathFilters": [
+ "./",
+ ":/global.json",
+ ":/Directory.Build.props"
+ ]
+}
diff --git a/Plugins/InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock.Consumer/WoofWare.Whippet.Plugin.InterfaceMock.Consumer.fsproj b/Plugins/InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock.Consumer/WoofWare.Whippet.Plugin.InterfaceMock.Consumer.fsproj
index 7cc005c..5339467 100644
--- a/Plugins/InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock.Consumer/WoofWare.Whippet.Plugin.InterfaceMock.Consumer.fsproj
+++ b/Plugins/InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock.Consumer/WoofWare.Whippet.Plugin.InterfaceMock.Consumer.fsproj
@@ -36,7 +36,7 @@
-
+
diff --git a/Plugins/InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock/README.md b/Plugins/InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock/README.md
index a410c0d..fb30ac6 100644
--- a/Plugins/InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock/README.md
+++ b/Plugins/InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock/README.md
@@ -80,3 +80,8 @@ You may supply an `isInternal : bool` argument:
By default, we make the resulting record type at most internal (never public),
since this is intended only to be used in tests;
but you can instead make it public by setting the `false` boolean.
+
+Instead of configuring the client with `InterfaceMock`,
+you may choose to add an attribute called `InterfaceMock` (with an optional "isInternal" argument)
+to any type you wish to use as an input.
+You may use `WoofWare.Whippet.Plugin.InterfaceMock.Attributes` to provide this attribute, or you may define it yourself.
diff --git a/Plugins/InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock.Test/TestSurface.fs b/Plugins/InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock.Test/TestSurface.fs
new file mode 100644
index 0000000..0731047
--- /dev/null
+++ b/Plugins/InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock.Test/TestSurface.fs
@@ -0,0 +1,26 @@
+namespace WoofWare.Whippet.Plugin.InterfaceMock.Test
+
+open NUnit.Framework
+open WoofWare.Whippet.Plugin.InterfaceMock
+open ApiSurface
+
+[]
+module TestAttributeSurface =
+ let assembly = typeof.Assembly
+
+ []
+ let ``Ensure API surface has not been modified`` () = ApiSurface.assertIdentical assembly
+
+ (*
+ []
+ let ``Check version against remote`` () =
+ MonotonicVersion.validate assembly "WoofWare.Whippet.Plugin.InterfaceMock.Attributes"
+ *)
+
+ []
+ let ``Update API surface`` () =
+ ApiSurface.writeAssemblyBaseline assembly
+
+ []
+ let ``Ensure public API is fully documented`` () =
+ DocCoverage.assertFullyDocumented assembly
diff --git a/Plugins/InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock.Test/WoofWare.Whippet.Plugin.InterfaceMock.Test.fsproj b/Plugins/InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock.Test/WoofWare.Whippet.Plugin.InterfaceMock.Test.fsproj
index a428a3d..309d41d 100644
--- a/Plugins/InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock.Test/WoofWare.Whippet.Plugin.InterfaceMock.Test.fsproj
+++ b/Plugins/InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock/WoofWare.Whippet.Plugin.InterfaceMock.Test/WoofWare.Whippet.Plugin.InterfaceMock.Test.fsproj
@@ -8,18 +8,21 @@
-
-
+
+
+
-
-
-
-
+
+
+
+
+
+
diff --git a/WoofWare.Whippet.sln b/WoofWare.Whippet.sln
index 95036ea..b663186 100644
--- a/WoofWare.Whippet.sln
+++ b/WoofWare.Whippet.sln
@@ -42,6 +42,8 @@ Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "WoofWare.Whippet.Plugin.Int
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "WoofWare.Whippet.Plugin.InterfaceMock.Test", "Plugins\InterfaceMock\WoofWare.Whippet.Plugin.InterfaceMock\WoofWare.Whippet.Plugin.InterfaceMock.Test\WoofWare.Whippet.Plugin.InterfaceMock.Test.fsproj", "{1B0D4C01-66CD-4E91-9DA5-9ED4F3319AFA}"
EndProject
+Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "WoofWare.Whippet.Plugin.InterfaceMock.Attributes", "Plugins\InterfaceMock\WoofWare.Whippet.Plugin.InterfaceMock.Attributes\WoofWare.Whippet.Plugin.InterfaceMock.Attributes.fsproj", "{E73EA066-2725-42CD-BE7D-39264C24AA97}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -132,5 +134,9 @@ Global
{1B0D4C01-66CD-4E91-9DA5-9ED4F3319AFA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1B0D4C01-66CD-4E91-9DA5-9ED4F3319AFA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1B0D4C01-66CD-4E91-9DA5-9ED4F3319AFA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E73EA066-2725-42CD-BE7D-39264C24AA97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E73EA066-2725-42CD-BE7D-39264C24AA97}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E73EA066-2725-42CD-BE7D-39264C24AA97}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E73EA066-2725-42CD-BE7D-39264C24AA97}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal