Skip to content

Commit

Permalink
[v2] Add source generator support for enum flags
Browse files Browse the repository at this point in the history
  • Loading branch information
henrikfroehling committed Mar 24, 2024
1 parent 9af8bb5 commit 5998a39
Show file tree
Hide file tree
Showing 6 changed files with 199 additions and 21 deletions.
26 changes: 26 additions & 0 deletions src/libs/Trakt.NET/Enums/TraktExtendedInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System.Text.Json.Serialization;
using TraktNET.SourceGenerators;

namespace TraktNET
{
[TraktEnum]
[TraktParameterEnum("extended")]
[Flags]
[JsonConverter(typeof(TraktExtendedInfoJsonConverter))]
public enum TraktExtendedInfo
{
[TraktEnumMemberJsonValue("")]
None = 0,

Metadata = 1,
Full = 2,

[TraktEnumMemberJsonValue("noseasons")]
NoSeasons = 4,

Episodes = 8,
GuestStars = 16,
Comments = 32,
VIP = 64
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ internal static class Constants

internal const string Header = @"//-----------------------------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by the Trakt.NET source generator.
// This code was generated by a Trakt.NET source generator.
// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
// </auto-generated>
//-----------------------------------------------------------------------------------------------------
Expand All @@ -19,5 +19,7 @@ internal static class Constants
#else
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
#endif";

internal const string GeneratedFilenameSuffix = ".g.cs";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,23 @@ internal static class EnumConstants

internal const string TraktEnumMemberJsonValueAttributeName = "TraktEnumMemberJsonValueAttribute";

internal const string TraktParameterEnumAttributeName = "TraktParameterEnumAttribute";

internal const string FullTraktEnumAttributeName = Constants.Namespace + "." + TraktEnumAttributeName;

internal const string FullTraktEnumMemberJsonValueAttributeName = Constants.Namespace + "." + TraktEnumMemberJsonValueAttributeName;

internal const string GeneratedTraktEnumAttributeFilename = TraktEnumAttributeName + ".g.cs";
internal const string FullTraktParameterEnumAttributeName = Constants.Namespace + "." + TraktParameterEnumAttributeName;

internal const string FullSystemFlagsAttributeName = "System.FlagsAttribute";

internal const string GeneratedTraktEnumAttributeFilename = TraktEnumAttributeName + Constants.GeneratedFilenameSuffix;

internal const string GeneratedTraktEnumMemberJsonValueAttributeFilename = TraktEnumMemberJsonValueAttributeName + ".g.cs";
internal const string GeneratedTraktEnumMemberJsonValueAttributeFilename = TraktEnumMemberJsonValueAttributeName + Constants.GeneratedFilenameSuffix;

internal const string GeneratedTraktEnumFileExtension = "EnumExtensions.g.cs";
internal const string GeneratedTraktParameterEnumAttributeFilename = TraktParameterEnumAttributeName + Constants.GeneratedFilenameSuffix;

internal const string GeneratedTraktEnumFileExtension = "EnumExtensions" + Constants.GeneratedFilenameSuffix;

internal const string TraktEnumAttribute = Constants.Header + @"
namespace " + Constants.Namespace + @"
Expand All @@ -38,17 +46,29 @@ namespace " + Constants.Namespace + @"
[global::System.AttributeUsage(global::System.AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
public sealed class " + TraktEnumMemberJsonValueAttributeName + @": global::System.Attribute
{
private string _jsonValue;
public " + TraktEnumMemberJsonValueAttributeName + @"(string jsonValue)
{
_jsonValue = jsonValue;
}
=> JsonValue = jsonValue;
public string JsonValue => _jsonValue;
public string JsonValue { get; }
public string? " + TraktEnumMemberJsonValuePropertyDisplayName + @" { get; set; }
}
}
";

internal const string TraktParameterEnumAttribute = Constants.Header + @"
namespace " + Constants.Namespace + @"
{
///<summary>Provides extension methods for an enum which can be used as a request parameter.</summary>
" + Constants.ExcludeCodeCoverage + @"
[global::System.AttributeUsage(global::System.AttributeTargets.Enum, AllowMultiple = false, Inherited = false)]
public sealed class " + TraktParameterEnumAttributeName + @": global::System.Attribute
{
public " + TraktParameterEnumAttributeName + @"(string uriParameterName)
=> UriParameterName = uriParameterName;
public string UriParameterName { get; }
}
}
";

internal static class TrackingNames
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,16 @@ namespace TraktNET.SourceGenerators.Enums
{
internal static class EnumSourceGenerationHelper
{
private const string UnspecifiedValue = "Unspecified";
private const string NoneValue = "None";

internal static string GenerateEnumExtensionClass(StringBuilder stringBuilder, in TraktEnumToGenerate enumToGenerate)
{
stringBuilder.Clear();

bool hasUnspecifiedMember = enumToGenerate.Values.Any(m => m.Name == UnspecifiedValue);
bool hasNoneMember = enumToGenerate.Values.Any(m => m.Name == NoneValue);

stringBuilder.Append(Constants.Header);
stringBuilder.Append(@"
using System.Text.Json;
Expand Down Expand Up @@ -35,13 +41,13 @@ public static partial class ").Append(enumToGenerate.EnumExtensionClassName);

foreach (TraktEnumMemberToGenerate member in enumToGenerate.Values)
{
if (member.Name == "Unspecified")
if ((member.Name == UnspecifiedValue || member.Name == NoneValue) && !member.HasTraktEnumMemberAttribute)
{
stringBuilder.Append(@"
").Append(enumToGenerate.Name).Append('.').Append(member.Name)
.Append(" => null,");
}
else if (member.HasAttribute && !string.IsNullOrWhiteSpace(member.JsonValue))
else if (member.HasTraktEnumMemberAttribute && member.JsonValue != null)
{
stringBuilder.Append(@"
").Append(enumToGenerate.Name).Append('.').Append(member.Name)
Expand Down Expand Up @@ -73,7 +79,7 @@ public static partial class ").Append(enumToGenerate.EnumExtensionClassName);

foreach (TraktEnumMemberToGenerate member in enumToGenerate.Values)
{
if (member.HasAttribute && !string.IsNullOrWhiteSpace(member.JsonValue))
if (member.HasTraktEnumMemberAttribute && member.JsonValue != null)
{
stringBuilder.Append(@"
").Append($"\"{member.JsonValue}\" => {enumToGenerate.Name}.{member.Name},");
Expand All @@ -85,8 +91,16 @@ public static partial class ").Append(enumToGenerate.EnumExtensionClassName);
}
}

stringBuilder.Append(@"
_ => ").Append(enumToGenerate.Name).Append(@".Unspecified,");
if (hasUnspecifiedMember)
{
stringBuilder.Append(@"
_ => ").Append(enumToGenerate.Name).Append('.').Append(UnspecifiedValue).Append(',');
}
else if (hasNoneMember)
{
stringBuilder.Append(@"
_ => ").Append(enumToGenerate.Name).Append('.').Append(NoneValue).Append(',');
}

stringBuilder.Append(@"
};");
Expand All @@ -103,7 +117,7 @@ public static partial class ").Append(enumToGenerate.EnumExtensionClassName);

foreach (TraktEnumMemberToGenerate member in enumToGenerate.Values)
{
if (member.HasAttribute && !string.IsNullOrWhiteSpace(member.DisplayName))
if (member.HasTraktEnumMemberAttribute && !string.IsNullOrWhiteSpace(member.DisplayName))
{
stringBuilder.Append(@"
").Append(enumToGenerate.Name).Append('.').Append(member.Name).Append(" => ").Append('"').Append(member.DisplayName).Append(@""",");
Expand All @@ -121,6 +135,87 @@ public static partial class ").Append(enumToGenerate.EnumExtensionClassName);
stringBuilder.Append(@"
};");

if (enumToGenerate.HasFlagsAttribute)
{
stringBuilder.Append(@"
/// <summary>Determines whether one or more bit fields are set in <see cref=""").Append(enumToGenerate.Name).Append(@""" />.</summary>
public static bool HasFlagSet(this ").Append(enumToGenerate.Name).Append(" value, ").Append(enumToGenerate.Name).Append(@" flag)
=> flag == 0 ? true : (value & flag) == flag;");
}

if (enumToGenerate.HasParameterEnumAttribute)
{
stringBuilder.Append(@"
/// <summary>Converts a <see cref=""").Append(enumToGenerate.Name).Append(@""" /> to a valid URI path value.</summary>
public static string ToUriPath(this ").Append(enumToGenerate.Name).Append(@" value)
{");
string invalidValueMember = string.Empty;

if (hasUnspecifiedMember)
invalidValueMember = UnspecifiedValue;
else if (hasNoneMember)
invalidValueMember = NoneValue;

if (hasUnspecifiedMember || hasNoneMember)
{
if (enumToGenerate.HasFlagsAttribute)
{
stringBuilder.Append(@"
if (value.HasFlagSet(").Append(enumToGenerate.Name).Append('.').Append(invalidValueMember).Append(@"))
return string.Empty;
");
}
else
{
stringBuilder.Append(@"
if (value == ").Append(enumToGenerate.Name).Append('.').Append(invalidValueMember).Append(@")
return string.Empty;
");
}
}

if (enumToGenerate.HasFlagsAttribute)
{
stringBuilder.Append(@"
var values = new List<string>();");

foreach (TraktEnumMemberToGenerate member in enumToGenerate.Values)
{
if (member.Name == invalidValueMember)
continue;

stringBuilder.Append(@"
if (value.HasFlagSet(").Append(enumToGenerate.Name).Append('.').Append(member.Name).Append(@"))
values.Add(").Append(enumToGenerate.Name).Append('.').Append(member.Name).Append(@".ToJson()!);");
}

stringBuilder.Append(@"
return ");

if (!string.IsNullOrWhiteSpace(enumToGenerate.ParameterEnumAttributeValue))
stringBuilder.Append('"').Append(enumToGenerate.ParameterEnumAttributeValue).Append(@"="" + ");

stringBuilder.Append(@"string.Join(',', values);");
}
else
{
stringBuilder.Append(@"
return ");

if (!string.IsNullOrWhiteSpace(enumToGenerate.ParameterEnumAttributeValue))
stringBuilder.Append('"').Append(enumToGenerate.ParameterEnumAttributeValue).Append(@"="" + ");

stringBuilder.Append(@"value.ToJson();");
}

stringBuilder.Append(@"
}");
}

stringBuilder.Append(@"
}
");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using System;
using System.Collections.Immutable;
using System.Text;
using System.Text.Json.Nodes;

namespace TraktNET.SourceGenerators.Enums
{
Expand All @@ -17,6 +19,9 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
context.RegisterPostInitializationOutput(ctx => ctx.AddSource(EnumConstants.GeneratedTraktEnumMemberJsonValueAttributeFilename,
SourceText.From(EnumConstants.TraktEnumMemberJsonValueAttribute, Encoding.UTF8)));

context.RegisterPostInitializationOutput(ctx => ctx.AddSource(EnumConstants.GeneratedTraktParameterEnumAttributeFilename,
SourceText.From(EnumConstants.TraktParameterEnumAttribute, Encoding.UTF8)));

IncrementalValuesProvider<TraktEnumToGenerate?> enumValuesProvider = context.SyntaxProvider
.ForAttributeWithMetadataName(
EnumConstants.FullTraktEnumAttributeName,
Expand All @@ -40,10 +45,30 @@ public void Initialize(IncrementalGeneratorInitializationContext context)

cancellationToken.ThrowIfCancellationRequested();

ImmutableArray<AttributeData> enumAttributes = enumSymbol.GetAttributes();

AttributeData? flagsAttribute = enumAttributes
.FirstOrDefault(attr => attr?.AttributeClass?.ToDisplayString() == EnumConstants.FullSystemFlagsAttributeName);

AttributeData? traktParameterEnumAttribute = enumAttributes
.FirstOrDefault(attr => attr?.AttributeClass?.ToDisplayString() == EnumConstants.FullTraktParameterEnumAttributeName);

bool hasFlagsAttribute = flagsAttribute != null;
bool hasTraktParameterEnumAttribute = traktParameterEnumAttribute != null;
string? parameterEnumAttributeValue = string.Empty;

if (hasTraktParameterEnumAttribute)
{
ImmutableArray<TypedConstant> constructorArguments = traktParameterEnumAttribute!.ConstructorArguments;
parameterEnumAttributeValue = constructorArguments[0].Value as string;
}

string enumExtensionName = enumSymbol.Name + "Extensions";
ImmutableArray<ISymbol> enumMembers = enumSymbol.GetMembers();
var members = new List<TraktEnumMemberToGenerate>(enumMembers.Length);

cancellationToken.ThrowIfCancellationRequested();

foreach (ISymbol member in enumMembers)
{
if (member is not IFieldSymbol field || field.ConstantValue is null)
Expand Down Expand Up @@ -75,7 +100,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
members.Add(new TraktEnumMemberToGenerate(field.Name, hasAttribute, jsonValue!, displayName!));
}

return new TraktEnumToGenerate(enumSymbol.Name, enumExtensionName, members);
return new TraktEnumToGenerate(enumSymbol.Name, enumExtensionName, hasFlagsAttribute, hasTraktParameterEnumAttribute, parameterEnumAttributeValue!, members);
}

private static void Execute(SourceProductionContext context, (Compilation Left, ImmutableArray<TraktEnumToGenerate?> Right) tuple)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ internal readonly record struct TraktEnumMemberToGenerate
{
public readonly string Name;

public readonly bool HasAttribute;
public readonly bool HasTraktEnumMemberAttribute;

public readonly string JsonValue;

public readonly string DisplayName;

public TraktEnumMemberToGenerate(string name, bool hasAttribute, string jsonValue, string displayName)
public TraktEnumMemberToGenerate(string name, bool hasTraktEnumMemberAttribute, string jsonValue, string displayName)
{
Name = name;
HasAttribute = hasAttribute;
HasTraktEnumMemberAttribute = hasTraktEnumMemberAttribute;
JsonValue = jsonValue;
DisplayName = displayName;
}
Expand All @@ -25,12 +25,22 @@ internal readonly record struct TraktEnumToGenerate

public readonly string EnumExtensionClassName;

public readonly bool HasFlagsAttribute;

public readonly bool HasParameterEnumAttribute;

public readonly string ParameterEnumAttributeValue;

public readonly IList<TraktEnumMemberToGenerate> Values;

public TraktEnumToGenerate(string name, string enumExtensionClassName, IList<TraktEnumMemberToGenerate> values)
public TraktEnumToGenerate(string name, string enumExtensionClassName, bool hasFlagsAttribute, bool hasParameterEnumAttribute,
string parameterEnumAttributeValue, IList<TraktEnumMemberToGenerate> values)
{
Name = name;
EnumExtensionClassName = enumExtensionClassName;
HasFlagsAttribute = hasFlagsAttribute;
HasParameterEnumAttribute = hasParameterEnumAttribute;
ParameterEnumAttributeValue = parameterEnumAttributeValue;
Values = values;
}
}
Expand Down

0 comments on commit 5998a39

Please sign in to comment.