Skip to content

Commit

Permalink
[Refactoring v2] Add source generator for adding enum extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
henrikfroehling committed Jan 30, 2024
1 parent 6f7086a commit 5cebb37
Show file tree
Hide file tree
Showing 13 changed files with 335 additions and 7 deletions.
2 changes: 1 addition & 1 deletion src/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<IsTestUtilityProject>$(MSBuildProjectName.EndsWith('Tests.Utility'))</IsTestUtilityProject>

<NoWarn>1701;1702;1705;1591</NoWarn>
<Copyright>Copyright (c) 2016 - Current Henrik Fröhling and Contributors</Copyright>
<Copyright>Copyright (c) 2016 - now | Henrik Fröhling and Contributors</Copyright>

<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
Expand Down
2 changes: 1 addition & 1 deletion src/Directory.Build.targets
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project>
<Project>
<PropertyGroup Condition="'$(TargetFramework)' == 'net5.0'">
<DefineConstants>$(DefineConstants);NET5_0_OR_GREATER</DefineConstants>
</PropertyGroup>
Expand Down
17 changes: 16 additions & 1 deletion src/Trakt.NET.sln
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,16 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{12789CC6
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{0BCCCEE2-F3F0-41AF-855E-0671CC966898}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Trakt.NET.Core.Tests", "tests\Trakt.NET.Core.Tests\Trakt.NET.Core.Tests.csproj", "{BDB5D945-5E7D-43B2-B95C-93CF115EE252}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Trakt.NET.Core.Tests", "tests\Trakt.NET.Core.Tests\Trakt.NET.Core.Tests.csproj", "{BDB5D945-5E7D-43B2-B95C-93CF115EE252}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{AEDBA600-CDF3-4DDD-8506-AA04C49884E2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Trakt.NET.SourceGenerators", "tools\Trakt.NET.SourceGenerators\Trakt.NET.SourceGenerators.csproj", "{8CD99D13-25D1-4920-B629-843D2470669A}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{F9B8AD43-FEDA-408C-B843-1D1A03ED827E}"
ProjectSection(SolutionItems) = preProject
tools\Directory.Build.props = tools\Directory.Build.props
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand All @@ -104,6 +113,10 @@ Global
{BDB5D945-5E7D-43B2-B95C-93CF115EE252}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BDB5D945-5E7D-43B2-B95C-93CF115EE252}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BDB5D945-5E7D-43B2-B95C-93CF115EE252}.Release|Any CPU.Build.0 = Release|Any CPU
{8CD99D13-25D1-4920-B629-843D2470669A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8CD99D13-25D1-4920-B629-843D2470669A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8CD99D13-25D1-4920-B629-843D2470669A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8CD99D13-25D1-4920-B629-843D2470669A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -124,6 +137,8 @@ Global
{377A7F6A-A4B9-45F6-AB2A-492F0E1F71AD} = {3DC8C555-C99D-4991-B74A-C75E966703DB}
{12789CC6-7A48-4C0C-9E4E-3DCF6F2056B9} = {3DC8C555-C99D-4991-B74A-C75E966703DB}
{BDB5D945-5E7D-43B2-B95C-93CF115EE252} = {0BCCCEE2-F3F0-41AF-855E-0671CC966898}
{8CD99D13-25D1-4920-B629-843D2470669A} = {AEDBA600-CDF3-4DDD-8506-AA04C49884E2}
{F9B8AD43-FEDA-408C-B843-1D1A03ED827E} = {3DC8C555-C99D-4991-B74A-C75E966703DB}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7025A39E-11A6-4587-98F7-E887B941D5A0}
Expand Down
6 changes: 2 additions & 4 deletions src/libs/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
<PackageLicenseFile>LICENSE.md</PackageLicenseFile>
<PackageReleaseNotes>https://github.com/henrikfroehling/Trakt.NET/blob/develop/Changelog.md#130</PackageReleaseNotes>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<Copyright>Copyright (c) 2016 - Current Henrik Fröhling et al.</Copyright>
</PropertyGroup>

<ItemGroup>
Expand All @@ -28,13 +27,12 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
<PackageReference Include="Nerdbank.GitVersioning" Version="3.6.133" PrivateAssets="all" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0" />
<ItemGroup Condition="('$(TargetFramework)' == 'netstandard2.0') Or ('$(TargetFramework)' == 'netstandard2.1')">
<PackageReference Include="System.Text.Json" Version="8.0.1" />
</ItemGroup>

<PropertyGroup Condition="'$(GITHUB_ACTIONS)' == 'true'">
Expand Down
13 changes: 13 additions & 0 deletions src/libs/Trakt.NET/Enums/TraktAccessScope.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using TraktNET.SourceGenerators;

namespace TraktNET
{
[TraktSmartEnum]
public enum TraktAccessScope
{
Unspecified,
Private,
Friends,
Public
}
}
11 changes: 11 additions & 0 deletions src/libs/Trakt.NET/Enums/TraktAccessTokenType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using TraktNET.SourceGenerators;

namespace TraktNET
{
[TraktSmartEnum]
public enum TraktAccessTokenType
{
Unspecified,
Bearer
}
}
5 changes: 5 additions & 0 deletions src/libs/Trakt.NET/Trakt.NET.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,9 @@
<GenerateDocumentationFile Condition="'$(Configuration)' == 'Release'">true</GenerateDocumentationFile>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\tools\Trakt.NET.SourceGenerators\Trakt.NET.SourceGenerators.csproj"
OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>

</Project>
7 changes: 7 additions & 0 deletions src/tools/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />

<ItemGroup Condition="('$(TargetFramework)' == 'netstandard2.0') Or ('$(TargetFramework)' == 'netstandard2.1')">
<PackageReference Include="System.Text.Json" Version="8.0.1" />
</ItemGroup>
</Project>
38 changes: 38 additions & 0 deletions src/tools/Trakt.NET.SourceGenerators/Constants.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
namespace TraktNET.SourceGenerators
{
internal static class Constants
{
internal const string Header = @"//-----------------------------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by the Trakt.NET source generator.
// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
// </auto-generated>
//-----------------------------------------------------------------------------------------------------
#nullable enable
";

internal const string SmartEnumAttribute = Header + @"
namespace TraktNET.SourceGenerators
{
[global::System.AttributeUsage(global::System.AttributeTargets.Enum)]
#if NET5_0_OR_GREATER
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage(Justification = ""Generated by the Trakt.NET source generator."")]
#else
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
#endif
public class TraktSmartEnumAttribute : global::System.Attribute
{
}
}
";

internal const string FullTraktSmartEnumAttributeName = "TraktNET.SourceGenerators.TraktSmartEnumAttribute";

internal const string GeneratedSmartEnumAttributeFilename = "TraktSmartEnumAttribute.g.cs";

internal const string GeneratedSmartEnumFileExtension = "EnumExtensions.g.cs";

internal const string TraktSmartEnumAttributeName = "TraktSmartEnumAttribute";
}
}
124 changes: 124 additions & 0 deletions src/tools/Trakt.NET.SourceGenerators/SourceGenerationHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
using System.Globalization;
using System.Text;

namespace TraktNET.SourceGenerators
{
internal static class SourceGenerationHelper
{
internal static string GenerateEnumExtensionClass(StringBuilder stringBuilder, in TraktEnumToGenerate enumToGenerate)
{
string fullyQualifiedName = $"global::{enumToGenerate.FullyQualifiedName}";

stringBuilder.Clear();
stringBuilder.Append(Constants.Header);
stringBuilder.Append(@"
/// <summary>Extension methods for <see cref=""").Append(fullyQualifiedName).Append(@""" />.</summary>
");

stringBuilder.Append(@"#if NET5_0_OR_GREATER
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage(Justification = ""Generated by the Trakt.NET source generator."")]
#else
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
#endif");
stringBuilder.Append(@"
internal static partial class ").Append(enumToGenerate.CompleteName);
stringBuilder.Append(@"
{");

stringBuilder.Append(@"
/// <summary>Returns the Json value for <see cref=""").Append(fullyQualifiedName).Append(@""" />.</summary>");
stringBuilder.Append(@"
internal static string? ToJson(this ").Append(fullyQualifiedName).Append(" value)");

stringBuilder.Append(@"
=> value switch
{");

foreach (string member in enumToGenerate.Values)
{
if (member == "Unspecified")
{
stringBuilder.Append(@"
").Append(fullyQualifiedName).Append('.').Append(member)
.Append(" => null,");
}
else
{
stringBuilder.Append(@"
").Append(fullyQualifiedName).Append('.').Append(member)
.Append(" => ").Append('"').Append(member.ToLower(CultureInfo.InvariantCulture)).Append(@""",");
}
}

stringBuilder.Append(@"
_ => null,");

stringBuilder.Append(@"
};");

stringBuilder.Append(@"
/// <summary>
/// Returns a <see cref=""").Append(fullyQualifiedName).Append(@""" /> for the given value, if possible.
/// <para />
/// If not possible, the value <see cref=""").Append(fullyQualifiedName).Append(".Unspecified").Append(@""" /> will be returned.
/// </summary>");
stringBuilder.Append(@"
internal static ").Append(fullyQualifiedName).Append(" To").Append(enumToGenerate.Name).Append("(this string? value)");

stringBuilder.Append(@"
=> value switch
{");

foreach (string member in enumToGenerate.Values)
{
if (member == "Unspecified")
continue;

stringBuilder.Append(@"
").Append($"\"{member.ToLower(CultureInfo.InvariantCulture)}\" => {fullyQualifiedName}.{member},");

stringBuilder.Append(@"
").Append($"\"{member.ToUpper(CultureInfo.InvariantCulture)}\" => {fullyQualifiedName}.{member},");

stringBuilder.Append(@"
").Append($"\"{member}\" => {fullyQualifiedName}.{member},");
}

stringBuilder.Append(@"
_ => ").Append(fullyQualifiedName).Append(".Unspecified,");

stringBuilder.Append(@"
};");

stringBuilder.Append(@"
/// <summary>Returns the display name for <see cref=""").Append(fullyQualifiedName).Append(@""" />.</summary>");
stringBuilder.Append(@"
internal static string DisplayName(this ").Append(fullyQualifiedName).Append(" value)");

stringBuilder.Append(@"
=> value switch
{");

foreach (string member in enumToGenerate.Values)
{
stringBuilder.Append(@"
").Append(fullyQualifiedName).Append('.').Append(member)
.Append(" => ").Append('"').Append(member).Append(@""",");
}

stringBuilder.Append(@"
_ => value.ToString(),");

stringBuilder.Append(@"
};");

stringBuilder.Append(@"
}
");

return stringBuilder.ToString();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<Product>Trakt.NET.SourceGenerators</Product>
<Title>Trakt.NET.SourceGenerators</Title>
<TargetFramework>netstandard2.0</TargetFramework>
<IncludeBuildOutput>false</IncludeBuildOutput>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" PrivateAssets="all" />
</ItemGroup>

<ItemGroup>
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
</ItemGroup>
</Project>
21 changes: 21 additions & 0 deletions src/tools/Trakt.NET.SourceGenerators/TraktEnumToGenerate.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace TraktNET
{
public readonly record struct TraktEnumToGenerate
{
public readonly string Name;

Check warning on line 5 in src/tools/Trakt.NET.SourceGenerators/TraktEnumToGenerate.cs

View workflow job for this annotation

GitHub Actions / build-v2-alpha

Do not declare visible instance fields (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1051)

Check warning on line 5 in src/tools/Trakt.NET.SourceGenerators/TraktEnumToGenerate.cs

View workflow job for this annotation

GitHub Actions / build-v2-alpha

Do not declare visible instance fields (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1051)

public readonly string CompleteName;

Check warning on line 7 in src/tools/Trakt.NET.SourceGenerators/TraktEnumToGenerate.cs

View workflow job for this annotation

GitHub Actions / build-v2-alpha

Do not declare visible instance fields (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1051)

public readonly string FullyQualifiedName;

Check warning on line 9 in src/tools/Trakt.NET.SourceGenerators/TraktEnumToGenerate.cs

View workflow job for this annotation

GitHub Actions / build-v2-alpha

Do not declare visible instance fields (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1051)

public readonly IList<string> Values;

Check warning on line 11 in src/tools/Trakt.NET.SourceGenerators/TraktEnumToGenerate.cs

View workflow job for this annotation

GitHub Actions / build-v2-alpha

Do not declare visible instance fields (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1051)

public TraktEnumToGenerate(string name, string completeName, string fullyQualifiedName, IList<string> values)
{
Name = name;
CompleteName = completeName;
FullyQualifiedName = fullyQualifiedName;
Values = values;
}
}
}
Loading

0 comments on commit 5cebb37

Please sign in to comment.