From 255c5f9ccd55c1fd159cacec69eccb576ffb0cd6 Mon Sep 17 00:00:00 2001 From: Frederik91 <11948105+Frederik91@users.noreply.github.com> Date: Mon, 9 Jun 2025 08:58:41 +0200 Subject: [PATCH 1/2] Merge master and fix bug --- .github/workflows/dotnet.yml | 55 +++++--- .../Attributes/GenerateInterfaceAttribute.cs | 9 -- .../MakeInterface.Contracts.csproj | 29 ---- MakeInterface.Demo/Implementations/Class1.cs | 2 +- MakeInterface.Demo/Issues/TestIssue40.cs | 15 ++ MakeInterface.Demo/MakeInterface.Demo.csproj | 3 +- MakeInterface.Demo/Models/Class2.cs | 2 +- .../Attributes/GenerateInterfaceAttribute.cs | 2 +- MakeInterface.Generator/InterfaceGenerator.cs | 128 +++++++++++++----- .../MakeInterface.Generator.csproj | 2 +- .../InterfaceGeneratorTests.cs | 87 +++++++++++- .../MakeInterface.Tests.csproj | 3 +- ...e#GenerateInterfaceAttribute.g.verified.cs | 8 ++ ...ssInterfaceInheritance#ITest.g.verified.cs | 16 +++ ...e#GenerateInterfaceAttribute.g.verified.cs | 8 ++ ...ests.CreateInterface#IClass1.g.received.cs | 2 +- ...ests.CreateInterface#IClass1.g.verified.cs | 2 +- ...s#GenerateInterfaceAttribute.g.verified.cs | 8 ++ ...s#GenerateInterfaceAttribute.g.verified.cs | 8 ++ ...y#GenerateInterfaceAttribute.g.verified.cs | 8 ++ ...l#GenerateInterfaceAttribute.g.verified.cs | 8 ++ ...r#GenerateInterfaceAttribute.g.verified.cs | 8 ++ ...y#GenerateInterfaceAttribute.g.verified.cs | 8 ++ ...s#GenerateInterfaceAttribute.g.verified.cs | 8 ++ ...d#GenerateInterfaceAttribute.g.verified.cs | 8 ++ ...d#GenerateInterfaceAttribute.g.verified.cs | 8 ++ ...atorTests.RelayCommand#ITest.g.verified.cs | 6 + ...n#GenerateInterfaceAttribute.g.received.cs | 8 ++ ...n#GenerateInterfaceAttribute.g.verified.cs | 8 ++ ...dWithCancellationToken#ITest.g.received.cs | 30 ++++ ...dWithCancellationToken#ITest.g.verified.cs | 30 ++++ MakeInterface.Tests/TestHelper.cs | 6 +- MakeInterface.sln | 6 - readme.md | 46 +++++-- 34 files changed, 466 insertions(+), 119 deletions(-) delete mode 100644 MakeInterface.Contracts/Attributes/GenerateInterfaceAttribute.cs delete mode 100644 MakeInterface.Contracts/MakeInterface.Contracts.csproj create mode 100644 MakeInterface.Demo/Issues/TestIssue40.cs create mode 100644 MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.BaseClassInterfaceInheritance#GenerateInterfaceAttribute.g.verified.cs create mode 100644 MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.BaseClassInterfaceInheritance#ITest.g.verified.cs create mode 100644 MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.CreateInterface#GenerateInterfaceAttribute.g.verified.cs create mode 100644 MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.ExcludedMembers#GenerateInterfaceAttribute.g.verified.cs create mode 100644 MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.InherritInterfaces#GenerateInterfaceAttribute.g.verified.cs create mode 100644 MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.Method_Expression_Body#GenerateInterfaceAttribute.g.verified.cs create mode 100644 MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.NamedModel#GenerateInterfaceAttribute.g.verified.cs create mode 100644 MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.NotPublicSetter#GenerateInterfaceAttribute.g.verified.cs create mode 100644 MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.ObservableProperty#GenerateInterfaceAttribute.g.verified.cs create mode 100644 MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.OverridenMembers#GenerateInterfaceAttribute.g.verified.cs create mode 100644 MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.PropertyWithBackingField#GenerateInterfaceAttribute.g.verified.cs create mode 100644 MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.RelayCommand#GenerateInterfaceAttribute.g.verified.cs create mode 100644 MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.RelayCommandWithCancellationToken#GenerateInterfaceAttribute.g.received.cs create mode 100644 MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.RelayCommandWithCancellationToken#GenerateInterfaceAttribute.g.verified.cs create mode 100644 MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.RelayCommandWithCancellationToken#ITest.g.received.cs create mode 100644 MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.RelayCommandWithCancellationToken#ITest.g.verified.cs diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 81de602..708ea87 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -1,30 +1,49 @@ -# This workflow will build a .NET project -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net - name: .NET on: push: branches: [ "master" ] + tags: [ "v*" ] pull_request: branches: [ "master" ] jobs: build: - runs-on: windows-latest - steps: - - uses: actions/checkout@v3 - - name: Setup .NET - uses: actions/setup-dotnet@v3 - with: - dotnet-version: 8.0.x - - name: Restore dependencies - run: dotnet restore - - name: Build - run: dotnet build --no-restore --configuration Release - - name: Test - run: dotnet test --no-build --verbosity normal --configuration Release - - name: Push Package to NuGet.org - run: nuget push **\*.nupkg -Source 'https://api.nuget.org/v3/index.json' -ApiKey ${{secrets.NUGET_API_KEY}} -SkipDuplicate \ No newline at end of file + - uses: actions/checkout@v3 + + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: 8.0.x + + - name: Restore dependencies + run: dotnet restore + + - name: Determine package version + id: version + shell: bash + run: | + if [ "$GITHUB_REF_TYPE" = "tag" ]; then + echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT" + else + echo "version=0.1.0-ci.${GITHUB_RUN_NUMBER}" >> "$GITHUB_OUTPUT" + fi + + - name: Build + run: dotnet build --no-restore --configuration Release /p:Version=${{ steps.version.outputs.version }} + + - name: Test + run: dotnet test --no-build --verbosity normal --configuration Release + + - name: Pack + run: dotnet pack MakeInterface.Generator/MakeInterface.Generator.csproj --no-build --configuration Release /p:PackageVersion=${{ steps.version.outputs.version }} -o ./artifacts + + - name: Publish prerelease to GitHub Packages + if: github.event_name == 'push' && github.ref_type == 'branch' + run: dotnet nuget push ./artifacts/MakeInterface.Generator.${{ steps.version.outputs.version }}.nupkg --source "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json" --api-key ${{ secrets.GITHUB_TOKEN }} --skip-duplicate + + - name: Publish release to NuGet.org + if: github.ref_type == 'tag' + run: dotnet nuget push ./artifacts/MakeInterface.Generator.${{ steps.version.outputs.version }}.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_API_KEY }} --skip-duplicate diff --git a/MakeInterface.Contracts/Attributes/GenerateInterfaceAttribute.cs b/MakeInterface.Contracts/Attributes/GenerateInterfaceAttribute.cs deleted file mode 100644 index a6597dc..0000000 --- a/MakeInterface.Contracts/Attributes/GenerateInterfaceAttribute.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace MakeInterface.Contracts.Attributes; -public class GenerateInterfaceAttribute : Attribute -{ - public List? ExcludedMembers { get; set; } -} diff --git a/MakeInterface.Contracts/MakeInterface.Contracts.csproj b/MakeInterface.Contracts/MakeInterface.Contracts.csproj deleted file mode 100644 index 8a3a336..0000000 --- a/MakeInterface.Contracts/MakeInterface.Contracts.csproj +++ /dev/null @@ -1,29 +0,0 @@ - - - - netstandard2.0 - enable - 10 - - - - library - netstandard2.0 - MakeInterface.Contracts - 0.4.0 - Frederik Tegnander - COWI - Contains attriubutes to use with Interfaces.SourceGenerator - readme.md - LICENSE.txt - https://github.com/Frederik91/Interfaces.SourceGenerator - true - - - - - - - - - diff --git a/MakeInterface.Demo/Implementations/Class1.cs b/MakeInterface.Demo/Implementations/Class1.cs index 8255675..8818af2 100644 --- a/MakeInterface.Demo/Implementations/Class1.cs +++ b/MakeInterface.Demo/Implementations/Class1.cs @@ -1,6 +1,6 @@ using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; -using MakeInterface.Contracts.Attributes; +using MakeInterface; using MakeInterface.Demo.Models; namespace MakeInterface.Demo.Implementations; diff --git a/MakeInterface.Demo/Issues/TestIssue40.cs b/MakeInterface.Demo/Issues/TestIssue40.cs new file mode 100644 index 0000000..4537da9 --- /dev/null +++ b/MakeInterface.Demo/Issues/TestIssue40.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using MakeInterface; + +namespace TestDemo.Issues; + +[GenerateInterface] +internal partial class ViewModel : ObservableObject, IViewModel +{ + [RelayCommand] + private Task DoStuff(CancellationToken cancellationToken) + { + return Task.CompletedTask; + } +} diff --git a/MakeInterface.Demo/MakeInterface.Demo.csproj b/MakeInterface.Demo/MakeInterface.Demo.csproj index 1d24df1..4ff167a 100644 --- a/MakeInterface.Demo/MakeInterface.Demo.csproj +++ b/MakeInterface.Demo/MakeInterface.Demo.csproj @@ -1,7 +1,7 @@  - net7 + net8.0 enable enable false @@ -12,7 +12,6 @@ - diff --git a/MakeInterface.Demo/Models/Class2.cs b/MakeInterface.Demo/Models/Class2.cs index fe67f2d..17585d1 100644 --- a/MakeInterface.Demo/Models/Class2.cs +++ b/MakeInterface.Demo/Models/Class2.cs @@ -1,4 +1,4 @@ -using MakeInterface.Contracts.Attributes; +using MakeInterface; using MakeInterface.Demo.Implementations; namespace MakeInterface.Demo.Models; diff --git a/MakeInterface.Generator/Attributes/GenerateInterfaceAttribute.cs b/MakeInterface.Generator/Attributes/GenerateInterfaceAttribute.cs index b37c292..7615ee0 100644 --- a/MakeInterface.Generator/Attributes/GenerateInterfaceAttribute.cs +++ b/MakeInterface.Generator/Attributes/GenerateInterfaceAttribute.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Text; -namespace MakeInterface.Generator.Attributes; +namespace MakeInterface; public class GenerateInterfaceAttribute : Attribute { diff --git a/MakeInterface.Generator/InterfaceGenerator.cs b/MakeInterface.Generator/InterfaceGenerator.cs index 28dc4ae..4a8bae5 100644 --- a/MakeInterface.Generator/InterfaceGenerator.cs +++ b/MakeInterface.Generator/InterfaceGenerator.cs @@ -1,4 +1,4 @@ -using MakeInterface.Generator.Attributes; +using MakeInterface; using MakeInterface.Generator.Extensions; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; @@ -15,6 +15,19 @@ public class InterfaceGenerator : IIncrementalGenerator { public void Initialize(IncrementalGeneratorInitializationContext context) { + context.RegisterPostInitializationOutput(static ctx => + { + ctx.AddSource("GenerateInterfaceAttribute.g.cs", """ +// +namespace MakeInterface; + +internal sealed class GenerateInterfaceAttribute : System.Attribute +{ + public string[]? Exclude { get; set; } +} +"""); + }); + var classProvider = context.SyntaxProvider .CreateSyntaxProvider((node, _) => { @@ -123,7 +136,7 @@ private void Generate(SourceProductionContext ctx, (SemanticModel, CompilationUn foreach (var memberSyntax in classSyntax.Members) { - if (IsNotValidInterfaceNamber(memberSyntax.Modifiers)) + if (IsNotValidInterfaceMember(memberSyntax.Modifiers)) continue; var name = memberSyntax.GetName(); @@ -287,7 +300,7 @@ private static List GetMembersFromType(ITypeSymbol baseType) foreach (var member in baseType.GetMembers()) { - if (baseType.TypeKind == TypeKind.Class && IsNotValidInterfaceNamber(member)) + if (baseType.TypeKind == TypeKind.Class && IsNotValidInterfaceMember(member)) continue; if (member is IMethodSymbol methodSymbol && methodSymbol.MethodKind is MethodKind.PropertyGet or MethodKind.PropertySet) @@ -301,59 +314,72 @@ private static List GetMembersFromType(ITypeSymbol baseType) private InterfaceDeclarationSyntax AddInterfaces(InterfaceDeclarationSyntax interfaceDeclaration, ClassDeclarationSyntax classSyntax, SemanticModel semanticModel) { - var baseInterfaces = classSyntax.BaseList?.Types + var baseTypes = classSyntax.BaseList?.Types .OfType() .ToArray(); - if (baseInterfaces?.Any() != true) + if (baseTypes?.Any() != true) return interfaceDeclaration; - var interfaces = baseInterfaces - .Select(x => semanticModel.GetSymbolInfo(x.Type).Symbol) - .OfType() - .Where(x => x.TypeKind == TypeKind.Interface && x.Name != interfaceDeclaration.Identifier.Text) - .ToArray(); - - if (!interfaces.Any()) - return interfaceDeclaration; + var interfacesSymbols = new List(); + var baseListTypes = new List(); - baseInterfaces = baseInterfaces.Where(x => interfaces.Any(y => semanticModel.IsSameType(x, y))).ToArray(); - interfaceDeclaration = interfaceDeclaration.AddBaseListTypes(baseInterfaces); - foreach (var @interface in interfaces) + foreach (var baseType in baseTypes) { - var interfaceImplementationSyntax = baseInterfaces.FirstOrDefault(x => semanticModel.IsSameType(x, @interface)); - if (interfaceImplementationSyntax is null) + var symbol = semanticModel.GetSymbolInfo(baseType.Type).Symbol as INamedTypeSymbol; + if (symbol is null) continue; - // Get members from interface - var baseInterfaceMembers = @interface.GetMembers().Select(x => x.Name); + if (symbol.TypeKind == TypeKind.Interface && symbol.Name != interfaceDeclaration.Identifier.Text) + { + interfacesSymbols.Add(symbol); + baseListTypes.Add(baseType); + } + else if (symbol.TypeKind == TypeKind.Class && ContainsAttributeWithName(symbol, nameof(GenerateInterfaceAttribute))) + { + interfacesSymbols.Add(symbol); + var interfaceName = GetInterfaceName(symbol); + baseListTypes.Add(SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName(interfaceName))); + } + } + + if (!baseListTypes.Any()) + return interfaceDeclaration; + + interfaceDeclaration = interfaceDeclaration.AddBaseListTypes(baseListTypes.ToArray()); - // Get members that matches interfaceMembers + foreach (var symbol in interfacesSymbols) + { + var members = GetMembersFromType(symbol); var membersToRemove = interfaceDeclaration.Members - .Where(member => member.GetName() is { } name && baseInterfaceMembers.Contains(name)) + .Where(member => member.GetName() is { } name && members.Contains(name)) .ToList(); - // Remove the members from the interface declaration. var newInterface = interfaceDeclaration.RemoveNodes(membersToRemove, SyntaxRemoveOptions.KeepNoTrivia); if (newInterface is not null) interfaceDeclaration = newInterface; } - // Add the base interfaces to the interface declaration's base list. return interfaceDeclaration; - } - + } + private MemberDeclarationSyntax CreateRelayCommand(MethodDeclarationSyntax methodSyntax) { var isAsync = MethodIsAsync(methodSyntax); TypeSyntax returnType; - if (methodSyntax.ParameterList.Parameters.Any()) + + // Filter out CancellationToken parameters as they don't affect the generic signature + var parametersForGeneric = methodSyntax.ParameterList.Parameters + .Where(p => !IsCancellationTokenParameter(p)) + .ToArray(); + + if (parametersForGeneric.Any()) { var genericReturnType = SyntaxFactory.GenericName("global::CommunityToolkit.Mvvm.Input." + (isAsync ? "IAsyncRelayCommand" : "IRelayCommand")); // Get the list of type arguments for the generic name syntax. var typeArgumentList = genericReturnType.TypeArgumentList; - typeArgumentList = typeArgumentList.AddArguments(methodSyntax.ParameterList.Parameters.Select(p => p.Type).OfType().ToArray()); + typeArgumentList = typeArgumentList.AddArguments(parametersForGeneric.Select(p => p.Type).OfType().ToArray()); returnType = genericReturnType.WithTypeArgumentList(typeArgumentList); } else @@ -382,15 +408,27 @@ private bool MethodIsAsync(MethodDeclarationSyntax methodSyntax) if (methodSyntax.Modifiers.Any(x => x.IsKind(SyntaxKind.AsyncKeyword))) return true; - return methodSyntax.ReturnType is GenericNameSyntax genericName && genericName.Identifier.Text == "Task" || methodSyntax.ReturnType.ToString() == "Task"; + return IsTaskReturnType(methodSyntax.ReturnType); } - private static bool IsNotValidInterfaceNamber(ISymbol member) + private static bool IsTaskReturnType(TypeSyntax typeSyntax) + { + return typeSyntax switch + { + IdentifierNameSyntax name => name.Identifier.Text == "Task", + GenericNameSyntax generic => generic.Identifier.Text == "Task", + QualifiedNameSyntax qualified => IsTaskReturnType(qualified.Right), + AliasQualifiedNameSyntax alias => IsTaskReturnType(alias.Name), + _ => false, + }; + } + + private static bool IsNotValidInterfaceMember(ISymbol member) { return !member.IsDefinition || member.IsStatic || member.IsImplicitlyDeclared || member.IsOverride; } - private static bool IsNotValidInterfaceNamber(SyntaxTokenList tokenList) + private static bool IsNotValidInterfaceMember(SyntaxTokenList tokenList) { if (tokenList.IsStatic()) { @@ -432,6 +470,20 @@ private string ConvertToPascalCase(string camelCase) return string.Join("", words.Select(w => w.Substring(0, 1).ToUpper() + w.Substring(1))); } + private string GetInterfaceName(INamedTypeSymbol classSymbol) + { + var name = classSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); + var index = name.LastIndexOf('.'); + if (index >= 0) + { + var prefix = name.Substring(0, index + 1); + var type = name.Substring(index + 1); + return prefix + "I" + type; + } + + return "I" + name; + } + private bool ContainsAttributeWithName(ISymbol member, string attrubuteName) { foreach (var attribute in member.GetAttributes()) @@ -460,4 +512,18 @@ private bool ContainsAttributeWithName(SyntaxList attribute } return false; } + + private bool IsCancellationTokenParameter(ParameterSyntax parameter) + { + if (parameter.Type is null) + return false; + + var typeName = parameter.Type.ToString(); + + // Check for various ways CancellationToken might be referenced + return typeName == "CancellationToken" || + typeName == "System.Threading.CancellationToken" || + typeName == "global::System.Threading.CancellationToken" || + typeName.EndsWith(".CancellationToken"); + } } diff --git a/MakeInterface.Generator/MakeInterface.Generator.csproj b/MakeInterface.Generator/MakeInterface.Generator.csproj index 650d887..3272971 100644 --- a/MakeInterface.Generator/MakeInterface.Generator.csproj +++ b/MakeInterface.Generator/MakeInterface.Generator.csproj @@ -12,7 +12,7 @@ library netstandard2.0 MakeInterface.Generator - 0.4.1 + 0.4.1-preview1 Frederik Tegnander COWI Interfaces;SourceGenerator;MakeInterface diff --git a/MakeInterface.Tests/InterfaceGeneratorTests.cs b/MakeInterface.Tests/InterfaceGeneratorTests.cs index 96517c6..6c52434 100644 --- a/MakeInterface.Tests/InterfaceGeneratorTests.cs +++ b/MakeInterface.Tests/InterfaceGeneratorTests.cs @@ -15,7 +15,7 @@ public Task CreateInterface() var source = """ #nullable enable using MakeInterface.Tests.Models; -using MakeInterface.Contracts.Attributes; +using MakeInterface; using System.Collections.Generic; namespace MakeInterface.Tests { @@ -187,7 +187,65 @@ private void Test2() { } [RelayCommand] private void Test7(string _) { } - } + + [RelayCommand] + private System.Threading.Tasks.Task Test8() { return System.Threading.Tasks.Task.CompletedTask; } + + [RelayCommand] + private global::System.Threading.Tasks.Task Test9() { return global::System.Threading.Tasks.Task.CompletedTask; } + } +} +"""; + + return TestHelper.Verify(source); + } + + [Fact] + public Task RelayCommandWithCancellationToken() + { + var source = """ +using System.Threading; +using System.Threading.Tasks; + +namespace MakeInterface.Tests +{ + [GenerateInterface] + public class ViewModel + { + [RelayCommand] + private Task DoStuff(CancellationToken cancellationToken) + { + return Task.CompletedTask; + } + + [RelayCommand] + private Task DoStuffWithParam(string parameter, CancellationToken cancellationToken) + { + return Task.CompletedTask; + } + + [RelayCommand] + private Task DoStuffWithSystemThreadingCancellationToken(System.Threading.CancellationToken cancellationToken) + { + return Task.CompletedTask; + } + + [RelayCommand] + private Task DoStuffWithGlobalCancellationToken(global::System.Threading.CancellationToken cancellationToken) + { + return Task.CompletedTask; + } + + [RelayCommand] + private void SyncWithCancellationToken(CancellationToken cancellationToken) + { + } + + [RelayCommand] + private void SyncWithParamAndCancellationToken(string parameter, CancellationToken cancellationToken) + { + } + } } """; @@ -312,7 +370,30 @@ namespace MakeInterface.Tests [GenerateInterface] public class Class { - public string Get() => return "foo"; + public string Get() => "foo"; + } +} +"""; + + return TestHelper.Verify(source); + } + + [Fact] + public Task BaseClassInterfaceInheritance() + { + var source = """ +namespace MakeInterface.Tests +{ + [GenerateInterface] + public class MyBase : IMyBase + { + public string Name { get; set; } + } + + [GenerateInterface] + public class MySub : MyBase, IMySub + { + public int Number { get; set; } } } """; diff --git a/MakeInterface.Tests/MakeInterface.Tests.csproj b/MakeInterface.Tests/MakeInterface.Tests.csproj index 96e00e0..57e81d2 100644 --- a/MakeInterface.Tests/MakeInterface.Tests.csproj +++ b/MakeInterface.Tests/MakeInterface.Tests.csproj @@ -1,7 +1,7 @@  - net6.0 + net8.0 enable 11 enable @@ -27,7 +27,6 @@ - diff --git a/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.BaseClassInterfaceInheritance#GenerateInterfaceAttribute.g.verified.cs b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.BaseClassInterfaceInheritance#GenerateInterfaceAttribute.g.verified.cs new file mode 100644 index 0000000..311afa7 --- /dev/null +++ b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.BaseClassInterfaceInheritance#GenerateInterfaceAttribute.g.verified.cs @@ -0,0 +1,8 @@ +//HintName: GenerateInterfaceAttribute.g.cs +// +namespace MakeInterface; + +internal sealed class GenerateInterfaceAttribute : System.Attribute +{ + public string[]? Exclude { get; set; } +} diff --git a/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.BaseClassInterfaceInheritance#ITest.g.verified.cs b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.BaseClassInterfaceInheritance#ITest.g.verified.cs new file mode 100644 index 0000000..2d82662 --- /dev/null +++ b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.BaseClassInterfaceInheritance#ITest.g.verified.cs @@ -0,0 +1,16 @@ +//HintName: ITest.g.cs +// +#pragma warning disable +#nullable enable +namespace MakeInterface.Tests +{ + public partial interface IMyBase + { + string Name { get; set; } + } + + public partial interface IMySub : global::MakeInterface.Tests.IMyBase + { + int Number { get; set; } + } +} diff --git a/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.CreateInterface#GenerateInterfaceAttribute.g.verified.cs b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.CreateInterface#GenerateInterfaceAttribute.g.verified.cs new file mode 100644 index 0000000..311afa7 --- /dev/null +++ b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.CreateInterface#GenerateInterfaceAttribute.g.verified.cs @@ -0,0 +1,8 @@ +//HintName: GenerateInterfaceAttribute.g.cs +// +namespace MakeInterface; + +internal sealed class GenerateInterfaceAttribute : System.Attribute +{ + public string[]? Exclude { get; set; } +} diff --git a/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.CreateInterface#IClass1.g.received.cs b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.CreateInterface#IClass1.g.received.cs index 9a74778..86f92a9 100644 --- a/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.CreateInterface#IClass1.g.received.cs +++ b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.CreateInterface#IClass1.g.received.cs @@ -1,7 +1,7 @@ //HintName: IClass1.g.cs #nullable enable using MakeInterface.Tests.Models; -using MakeInterface.Contracts.Attributes; +using MakeInterface; using System.Collections.Generic; // diff --git a/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.CreateInterface#IClass1.g.verified.cs b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.CreateInterface#IClass1.g.verified.cs index 9a74778..86f92a9 100644 --- a/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.CreateInterface#IClass1.g.verified.cs +++ b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.CreateInterface#IClass1.g.verified.cs @@ -1,7 +1,7 @@ //HintName: IClass1.g.cs #nullable enable using MakeInterface.Tests.Models; -using MakeInterface.Contracts.Attributes; +using MakeInterface; using System.Collections.Generic; // diff --git a/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.ExcludedMembers#GenerateInterfaceAttribute.g.verified.cs b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.ExcludedMembers#GenerateInterfaceAttribute.g.verified.cs new file mode 100644 index 0000000..311afa7 --- /dev/null +++ b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.ExcludedMembers#GenerateInterfaceAttribute.g.verified.cs @@ -0,0 +1,8 @@ +//HintName: GenerateInterfaceAttribute.g.cs +// +namespace MakeInterface; + +internal sealed class GenerateInterfaceAttribute : System.Attribute +{ + public string[]? Exclude { get; set; } +} diff --git a/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.InherritInterfaces#GenerateInterfaceAttribute.g.verified.cs b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.InherritInterfaces#GenerateInterfaceAttribute.g.verified.cs new file mode 100644 index 0000000..311afa7 --- /dev/null +++ b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.InherritInterfaces#GenerateInterfaceAttribute.g.verified.cs @@ -0,0 +1,8 @@ +//HintName: GenerateInterfaceAttribute.g.cs +// +namespace MakeInterface; + +internal sealed class GenerateInterfaceAttribute : System.Attribute +{ + public string[]? Exclude { get; set; } +} diff --git a/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.Method_Expression_Body#GenerateInterfaceAttribute.g.verified.cs b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.Method_Expression_Body#GenerateInterfaceAttribute.g.verified.cs new file mode 100644 index 0000000..311afa7 --- /dev/null +++ b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.Method_Expression_Body#GenerateInterfaceAttribute.g.verified.cs @@ -0,0 +1,8 @@ +//HintName: GenerateInterfaceAttribute.g.cs +// +namespace MakeInterface; + +internal sealed class GenerateInterfaceAttribute : System.Attribute +{ + public string[]? Exclude { get; set; } +} diff --git a/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.NamedModel#GenerateInterfaceAttribute.g.verified.cs b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.NamedModel#GenerateInterfaceAttribute.g.verified.cs new file mode 100644 index 0000000..311afa7 --- /dev/null +++ b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.NamedModel#GenerateInterfaceAttribute.g.verified.cs @@ -0,0 +1,8 @@ +//HintName: GenerateInterfaceAttribute.g.cs +// +namespace MakeInterface; + +internal sealed class GenerateInterfaceAttribute : System.Attribute +{ + public string[]? Exclude { get; set; } +} diff --git a/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.NotPublicSetter#GenerateInterfaceAttribute.g.verified.cs b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.NotPublicSetter#GenerateInterfaceAttribute.g.verified.cs new file mode 100644 index 0000000..311afa7 --- /dev/null +++ b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.NotPublicSetter#GenerateInterfaceAttribute.g.verified.cs @@ -0,0 +1,8 @@ +//HintName: GenerateInterfaceAttribute.g.cs +// +namespace MakeInterface; + +internal sealed class GenerateInterfaceAttribute : System.Attribute +{ + public string[]? Exclude { get; set; } +} diff --git a/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.ObservableProperty#GenerateInterfaceAttribute.g.verified.cs b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.ObservableProperty#GenerateInterfaceAttribute.g.verified.cs new file mode 100644 index 0000000..311afa7 --- /dev/null +++ b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.ObservableProperty#GenerateInterfaceAttribute.g.verified.cs @@ -0,0 +1,8 @@ +//HintName: GenerateInterfaceAttribute.g.cs +// +namespace MakeInterface; + +internal sealed class GenerateInterfaceAttribute : System.Attribute +{ + public string[]? Exclude { get; set; } +} diff --git a/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.OverridenMembers#GenerateInterfaceAttribute.g.verified.cs b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.OverridenMembers#GenerateInterfaceAttribute.g.verified.cs new file mode 100644 index 0000000..311afa7 --- /dev/null +++ b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.OverridenMembers#GenerateInterfaceAttribute.g.verified.cs @@ -0,0 +1,8 @@ +//HintName: GenerateInterfaceAttribute.g.cs +// +namespace MakeInterface; + +internal sealed class GenerateInterfaceAttribute : System.Attribute +{ + public string[]? Exclude { get; set; } +} diff --git a/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.PropertyWithBackingField#GenerateInterfaceAttribute.g.verified.cs b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.PropertyWithBackingField#GenerateInterfaceAttribute.g.verified.cs new file mode 100644 index 0000000..311afa7 --- /dev/null +++ b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.PropertyWithBackingField#GenerateInterfaceAttribute.g.verified.cs @@ -0,0 +1,8 @@ +//HintName: GenerateInterfaceAttribute.g.cs +// +namespace MakeInterface; + +internal sealed class GenerateInterfaceAttribute : System.Attribute +{ + public string[]? Exclude { get; set; } +} diff --git a/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.RelayCommand#GenerateInterfaceAttribute.g.verified.cs b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.RelayCommand#GenerateInterfaceAttribute.g.verified.cs new file mode 100644 index 0000000..311afa7 --- /dev/null +++ b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.RelayCommand#GenerateInterfaceAttribute.g.verified.cs @@ -0,0 +1,8 @@ +//HintName: GenerateInterfaceAttribute.g.cs +// +namespace MakeInterface; + +internal sealed class GenerateInterfaceAttribute : System.Attribute +{ + public string[]? Exclude { get; set; } +} diff --git a/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.RelayCommand#ITest.g.verified.cs b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.RelayCommand#ITest.g.verified.cs index 6c3bdbd..f79ea5b 100644 --- a/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.RelayCommand#ITest.g.verified.cs +++ b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.RelayCommand#ITest.g.verified.cs @@ -29,5 +29,11 @@ public partial interface IClass1 // This property was generated because of the RelayCommand attribute applied to the 'Test7' method. See https://aka.ms/CommunityToolkit.MVVM global::CommunityToolkit.Mvvm.Input.IRelayCommand Test7Command { get; } + + // This property was generated because of the RelayCommand attribute applied to the 'Test8' method. See https://aka.ms/CommunityToolkit.MVVM + global::CommunityToolkit.Mvvm.Input.IAsyncRelayCommand Test8Command { get; } + + // This property was generated because of the RelayCommand attribute applied to the 'Test9' method. See https://aka.ms/CommunityToolkit.MVVM + global::CommunityToolkit.Mvvm.Input.IAsyncRelayCommand Test9Command { get; } } } \ No newline at end of file diff --git a/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.RelayCommandWithCancellationToken#GenerateInterfaceAttribute.g.received.cs b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.RelayCommandWithCancellationToken#GenerateInterfaceAttribute.g.received.cs new file mode 100644 index 0000000..311afa7 --- /dev/null +++ b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.RelayCommandWithCancellationToken#GenerateInterfaceAttribute.g.received.cs @@ -0,0 +1,8 @@ +//HintName: GenerateInterfaceAttribute.g.cs +// +namespace MakeInterface; + +internal sealed class GenerateInterfaceAttribute : System.Attribute +{ + public string[]? Exclude { get; set; } +} diff --git a/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.RelayCommandWithCancellationToken#GenerateInterfaceAttribute.g.verified.cs b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.RelayCommandWithCancellationToken#GenerateInterfaceAttribute.g.verified.cs new file mode 100644 index 0000000..311afa7 --- /dev/null +++ b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.RelayCommandWithCancellationToken#GenerateInterfaceAttribute.g.verified.cs @@ -0,0 +1,8 @@ +//HintName: GenerateInterfaceAttribute.g.cs +// +namespace MakeInterface; + +internal sealed class GenerateInterfaceAttribute : System.Attribute +{ + public string[]? Exclude { get; set; } +} diff --git a/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.RelayCommandWithCancellationToken#ITest.g.received.cs b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.RelayCommandWithCancellationToken#ITest.g.received.cs new file mode 100644 index 0000000..156fd68 --- /dev/null +++ b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.RelayCommandWithCancellationToken#ITest.g.received.cs @@ -0,0 +1,30 @@ +//HintName: ITest.g.cs +using System.Threading; +using System.Threading.Tasks; + +// +#pragma warning disable +#nullable enable +namespace MakeInterface.Tests +{ + public partial interface IViewModel + { + // This property was generated because of the RelayCommand attribute applied to the 'DoStuff' method. See https://aka.ms/CommunityToolkit.MVVM + global::CommunityToolkit.Mvvm.Input.IAsyncRelayCommand DoStuffCommand { get; } + + // This property was generated because of the RelayCommand attribute applied to the 'DoStuffWithParam' method. See https://aka.ms/CommunityToolkit.MVVM + global::CommunityToolkit.Mvvm.Input.IAsyncRelayCommand DoStuffWithParamCommand { get; } + + // This property was generated because of the RelayCommand attribute applied to the 'DoStuffWithSystemThreadingCancellationToken' method. See https://aka.ms/CommunityToolkit.MVVM + global::CommunityToolkit.Mvvm.Input.IAsyncRelayCommand DoStuffWithSystemThreadingCancellationTokenCommand { get; } + + // This property was generated because of the RelayCommand attribute applied to the 'DoStuffWithGlobalCancellationToken' method. See https://aka.ms/CommunityToolkit.MVVM + global::CommunityToolkit.Mvvm.Input.IAsyncRelayCommand DoStuffWithGlobalCancellationTokenCommand { get; } + + // This property was generated because of the RelayCommand attribute applied to the 'SyncWithCancellationToken' method. See https://aka.ms/CommunityToolkit.MVVM + global::CommunityToolkit.Mvvm.Input.IRelayCommand SyncWithCancellationTokenCommand { get; } + + // This property was generated because of the RelayCommand attribute applied to the 'SyncWithParamAndCancellationToken' method. See https://aka.ms/CommunityToolkit.MVVM + global::CommunityToolkit.Mvvm.Input.IRelayCommand SyncWithParamAndCancellationTokenCommand { get; } + } +} diff --git a/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.RelayCommandWithCancellationToken#ITest.g.verified.cs b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.RelayCommandWithCancellationToken#ITest.g.verified.cs new file mode 100644 index 0000000..156fd68 --- /dev/null +++ b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.RelayCommandWithCancellationToken#ITest.g.verified.cs @@ -0,0 +1,30 @@ +//HintName: ITest.g.cs +using System.Threading; +using System.Threading.Tasks; + +// +#pragma warning disable +#nullable enable +namespace MakeInterface.Tests +{ + public partial interface IViewModel + { + // This property was generated because of the RelayCommand attribute applied to the 'DoStuff' method. See https://aka.ms/CommunityToolkit.MVVM + global::CommunityToolkit.Mvvm.Input.IAsyncRelayCommand DoStuffCommand { get; } + + // This property was generated because of the RelayCommand attribute applied to the 'DoStuffWithParam' method. See https://aka.ms/CommunityToolkit.MVVM + global::CommunityToolkit.Mvvm.Input.IAsyncRelayCommand DoStuffWithParamCommand { get; } + + // This property was generated because of the RelayCommand attribute applied to the 'DoStuffWithSystemThreadingCancellationToken' method. See https://aka.ms/CommunityToolkit.MVVM + global::CommunityToolkit.Mvvm.Input.IAsyncRelayCommand DoStuffWithSystemThreadingCancellationTokenCommand { get; } + + // This property was generated because of the RelayCommand attribute applied to the 'DoStuffWithGlobalCancellationToken' method. See https://aka.ms/CommunityToolkit.MVVM + global::CommunityToolkit.Mvvm.Input.IAsyncRelayCommand DoStuffWithGlobalCancellationTokenCommand { get; } + + // This property was generated because of the RelayCommand attribute applied to the 'SyncWithCancellationToken' method. See https://aka.ms/CommunityToolkit.MVVM + global::CommunityToolkit.Mvvm.Input.IRelayCommand SyncWithCancellationTokenCommand { get; } + + // This property was generated because of the RelayCommand attribute applied to the 'SyncWithParamAndCancellationToken' method. See https://aka.ms/CommunityToolkit.MVVM + global::CommunityToolkit.Mvvm.Input.IRelayCommand SyncWithParamAndCancellationTokenCommand { get; } + } +} diff --git a/MakeInterface.Tests/TestHelper.cs b/MakeInterface.Tests/TestHelper.cs index b4957c1..21ff158 100644 --- a/MakeInterface.Tests/TestHelper.cs +++ b/MakeInterface.Tests/TestHelper.cs @@ -6,7 +6,6 @@ using System.Text; using System.Threading.Tasks; using MakeInterface.Generator; -using MakeInterface.Contracts.Attributes; namespace MakeInterface.Tests; @@ -18,8 +17,7 @@ public static Task Verify(string source, string path = "Test.cs") var syntaxTree = CSharpSyntaxTree.ParseText(source, path: path); var references = new[] -{ - MetadataReference.CreateFromFile(typeof(GenerateInterfaceAttribute).Assembly.Location), + { MetadataReference.CreateFromFile(typeof(object).Assembly.Location) }; @@ -30,7 +28,7 @@ public static Task Verify(string source, string path = "Test.cs") references: references); // 👈 pass the references to the compilation - // Create an instance of our EnumGenerator incremental source generator + // Create an instance of our InterfaceGenerator incremental source generator var generator = new InterfaceGenerator(); // The GeneratorDriver is used to run our generator against a compilation diff --git a/MakeInterface.sln b/MakeInterface.sln index a18a6f1..c855942 100644 --- a/MakeInterface.sln +++ b/MakeInterface.sln @@ -5,8 +5,6 @@ VisualStudioVersion = 17.5.33209.295 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MakeInterface.Generator", "MakeInterface.Generator\MakeInterface.Generator.csproj", "{712EECDF-2DC5-482A-93CA-FF0FC041D3A6}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MakeInterface.Contracts", "MakeInterface.Contracts\MakeInterface.Contracts.csproj", "{BB99CEBA-CD89-4677-943A-2A2A006D9A31}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MakeInterface.Tests", "MakeInterface.Tests\MakeInterface.Tests.csproj", "{2FEB818B-0CE6-4E5D-B47D-69742E601079}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MakeInterface.Demo", "MakeInterface.Demo\MakeInterface.Demo.csproj", "{6C04F450-6C51-459C-AE5B-B7B22E3A779C}" @@ -26,10 +24,6 @@ Global {712EECDF-2DC5-482A-93CA-FF0FC041D3A6}.Debug|Any CPU.Build.0 = Debug|Any CPU {712EECDF-2DC5-482A-93CA-FF0FC041D3A6}.Release|Any CPU.ActiveCfg = Release|Any CPU {712EECDF-2DC5-482A-93CA-FF0FC041D3A6}.Release|Any CPU.Build.0 = Release|Any CPU - {BB99CEBA-CD89-4677-943A-2A2A006D9A31}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BB99CEBA-CD89-4677-943A-2A2A006D9A31}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BB99CEBA-CD89-4677-943A-2A2A006D9A31}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BB99CEBA-CD89-4677-943A-2A2A006D9A31}.Release|Any CPU.Build.0 = Release|Any CPU {2FEB818B-0CE6-4E5D-B47D-69742E601079}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2FEB818B-0CE6-4E5D-B47D-69742E601079}.Debug|Any CPU.Build.0 = Debug|Any CPU {2FEB818B-0CE6-4E5D-B47D-69742E601079}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/readme.md b/readme.md index 551ac51..fe1c4be 100644 --- a/readme.md +++ b/readme.md @@ -1,16 +1,32 @@ # MakeInterface -Creates an interface of a class using source generator +Generate interfaces for your classes at compile time using a simple attribute. [![.NET](https://github.com/Frederik91/MakeInterface/actions/workflows/dotnet.yml/badge.svg)](https://github.com/Frederik91/MakeInterface/actions/workflows/dotnet.yml) +MakeInterface is a [C# source generator](https://learn.microsoft.com/dotnet/csharp/roslyn-sdk/source-generators-overview) that produces an `I{ClassName}` interface for any class marked with `GenerateInterface`. The generator analyses the public members of the class and writes the matching interface into your project's build output. + +This is particularly useful when you simply need an interface to facilitate unit testing or dependency injection. + ## Usage -Add the attribute to the class you want to generate the interface for +1. Install the NuGet package (see [Installation](#installation)). +2. Add the attribute to the class you want an interface for. +3. Build your project. The interface will appear in your `obj` folder and be part of the compilation. ```csharp [GenerateInterface] public class MyClass { - public string MyProperty { get; set; } - public void MyMethod() { } + public string MyProperty { get; set; } + public void MyMethod() { } +} +``` + +Need to omit a member? Use the `Exclude` property to provide a list of member names: +```csharp +[GenerateInterface(Exclude = new[] { nameof(MyMethod) })] +public class MyClass +{ + public string MyProperty { get; set; } + public void MyMethod() { } } ``` @@ -27,16 +43,28 @@ You can then implement the interface in your class ```csharp public class MyClass : IMyClass { - public string MyProperty { get; set; } - public void MyMethod() { } + public string MyProperty { get; set; } + public void MyMethod() { } } ``` +## When should I generate interfaces? +Generating interfaces works well when you only need an interface so the class can be mocked in unit tests or injected into other components. In that scenario your class is typically the single implementation and keeping the interface in sync manually becomes boilerplate. Let the generator do the work for you. + +If you maintain many implementations of the same interface or the interface needs to diverge from the class surface, consider writing the interface yourself. Manually created interfaces give you more control over its shape and versioning. + ## Installation -Install the NuGet package [MakeInterface](https://www.nuget.org/packages/MakeInterface.Generator/) +Install the NuGet package [MakeInterface](https://www.nuget.org/packages/MakeInterface.Generator/): + +```bash +dotnet add package MakeInterface.Generator +``` -You can either create the attribute yourself or use the one provided in the package [MakeInterface.Contracts](https://www.nuget.org/packages/MakeInterface.Contracts/) +The `GenerateInterface` attribute is included in the package and will be available after the build without adding any extra references. ## License -MIT \ No newline at end of file +MIT +## Release process +- Pushes to the `master` branch publish prerelease packages to GitHub Packages using versions like `0.1.0-ci.`. +- Tagging the repository with `v` automatically publishes that version to NuGet.org if `NUGET_API_KEY` is configured. From af6128c136f2b4240a5fa16d114bf4a240ba0ae4 Mon Sep 17 00:00:00 2001 From: Frederik Tegnander Date: Tue, 10 Jun 2025 09:44:03 +0200 Subject: [PATCH 2/2] fix: reorder using directives in generated interface files --- ...ceGeneratorTests.CreateInterface#IClass1.g.verified.cs | 5 ----- ....RelayCommandWithCancellationToken#ITest.g.received.cs | 8 ++++---- ....RelayCommandWithCancellationToken#ITest.g.verified.cs | 8 ++++---- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.CreateInterface#IClass1.g.verified.cs b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.CreateInterface#IClass1.g.verified.cs index 58ae0fe..875de87 100644 --- a/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.CreateInterface#IClass1.g.verified.cs +++ b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.CreateInterface#IClass1.g.verified.cs @@ -1,9 +1,4 @@ //HintName: IClass1.g.cs -#nullable enable -using MakeInterface.Tests.Models; -using MakeInterface; -using System.Collections.Generic; - // #pragma warning disable #nullable enable diff --git a/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.RelayCommandWithCancellationToken#ITest.g.received.cs b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.RelayCommandWithCancellationToken#ITest.g.received.cs index 156fd68..38c46b5 100644 --- a/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.RelayCommandWithCancellationToken#ITest.g.received.cs +++ b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.RelayCommandWithCancellationToken#ITest.g.received.cs @@ -1,10 +1,10 @@ //HintName: ITest.g.cs -using System.Threading; -using System.Threading.Tasks; - // #pragma warning disable #nullable enable +using System.Threading; +using System.Threading.Tasks; + namespace MakeInterface.Tests { public partial interface IViewModel @@ -27,4 +27,4 @@ public partial interface IViewModel // This property was generated because of the RelayCommand attribute applied to the 'SyncWithParamAndCancellationToken' method. See https://aka.ms/CommunityToolkit.MVVM global::CommunityToolkit.Mvvm.Input.IRelayCommand SyncWithParamAndCancellationTokenCommand { get; } } -} +} \ No newline at end of file diff --git a/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.RelayCommandWithCancellationToken#ITest.g.verified.cs b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.RelayCommandWithCancellationToken#ITest.g.verified.cs index 156fd68..38c46b5 100644 --- a/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.RelayCommandWithCancellationToken#ITest.g.verified.cs +++ b/MakeInterface.Tests/Snapshots/InterfaceGeneratorTests.RelayCommandWithCancellationToken#ITest.g.verified.cs @@ -1,10 +1,10 @@ //HintName: ITest.g.cs -using System.Threading; -using System.Threading.Tasks; - // #pragma warning disable #nullable enable +using System.Threading; +using System.Threading.Tasks; + namespace MakeInterface.Tests { public partial interface IViewModel @@ -27,4 +27,4 @@ public partial interface IViewModel // This property was generated because of the RelayCommand attribute applied to the 'SyncWithParamAndCancellationToken' method. See https://aka.ms/CommunityToolkit.MVVM global::CommunityToolkit.Mvvm.Input.IRelayCommand SyncWithParamAndCancellationTokenCommand { get; } } -} +} \ No newline at end of file