diff --git a/Funcky.DiscriminatedUnion.SourceGeneration/CollectingInterpolatedStringHandler.cs b/Funcky.DiscriminatedUnion.SourceGeneration/CollectingInterpolatedStringHandler.cs new file mode 100644 index 0000000..e2fa831 --- /dev/null +++ b/Funcky.DiscriminatedUnion.SourceGeneration/CollectingInterpolatedStringHandler.cs @@ -0,0 +1,28 @@ +using System.Runtime.CompilerServices; + +namespace Funcky.DiscriminatedUnion.SourceGeneration; + +[InterpolatedStringHandler] +public readonly struct CollectingInterpolatedStringHandler +{ + public CollectingInterpolatedStringHandler(int literalLength, int formattedCount) + { + _items = new List(formattedCount * 2); + } + + public CollectingInterpolatedStringHandler() + { + _items = new List(); + } + + private readonly List _items; + + public IEnumerable GetItems() => _items; + + public void AppendLiteral(string s) => _items.Add(s); + + public void AppendFormatted(T t) => _items.Add(t); + + public void AppendFormatted(CollectingInterpolatedStringHandler handler) + => _items.AddRange(handler._items); +} diff --git a/Funcky.DiscriminatedUnion.SourceGeneration/CollectingInterpolatedStringHandlerExtensions.cs b/Funcky.DiscriminatedUnion.SourceGeneration/CollectingInterpolatedStringHandlerExtensions.cs new file mode 100644 index 0000000..112b31c --- /dev/null +++ b/Funcky.DiscriminatedUnion.SourceGeneration/CollectingInterpolatedStringHandlerExtensions.cs @@ -0,0 +1,53 @@ +namespace Funcky.DiscriminatedUnion.SourceGeneration; + +public static class CollectingInterpolatedStringHandlerExtensions +{ + public static CollectingInterpolatedStringHandler JoinToInterpolation( + this IEnumerable source, + string separator) + { + using var enumerator = source.GetEnumerator(); + + if (!enumerator.MoveNext()) + { + return new CollectingInterpolatedStringHandler(); + } + + var result = new CollectingInterpolatedStringHandler(); + + result.AppendFormatted(enumerator.Current); + + while (enumerator.MoveNext()) + { + result.AppendLiteral(separator); + result.AppendFormatted(enumerator.Current); + } + + return result; + } + + public static CollectingInterpolatedStringHandler JoinToInterpolation( + this IEnumerable source, + Func createPart, + string separator) + { + using var enumerator = source.GetEnumerator(); + + if (!enumerator.MoveNext()) + { + return new CollectingInterpolatedStringHandler(); + } + + var result = new CollectingInterpolatedStringHandler(); + + result.AppendFormatted(createPart(enumerator.Current)); + + while (enumerator.MoveNext()) + { + result.AppendLiteral(separator); + result.AppendFormatted(createPart(enumerator.Current)); + } + + return result; + } +} diff --git a/Funcky.DiscriminatedUnion.SourceGeneration/DiscriminatedUnion.cs b/Funcky.DiscriminatedUnion.SourceGeneration/DiscriminatedUnion.cs index 3258ada..13eb39e 100644 --- a/Funcky.DiscriminatedUnion.SourceGeneration/DiscriminatedUnion.cs +++ b/Funcky.DiscriminatedUnion.SourceGeneration/DiscriminatedUnion.cs @@ -6,9 +6,10 @@ internal sealed record DiscriminatedUnion( TypeDeclarationSyntax Type, IReadOnlyList ParentTypes, string? Namespace, - string MethodVisibility, + string GeneratedMethodOrClassVisibility, string MatchResultTypeName, - IReadOnlyList Variants); + IReadOnlyList Variants, + bool GeneratePartitionExtension); internal sealed record DiscriminatedUnionVariant( TypeDeclarationSyntax Type, diff --git a/Funcky.DiscriminatedUnion.SourceGeneration/DiscriminatedUnionGenerator.cs b/Funcky.DiscriminatedUnion.SourceGeneration/DiscriminatedUnionGenerator.cs index c4e2169..07b6fa6 100644 --- a/Funcky.DiscriminatedUnion.SourceGeneration/DiscriminatedUnionGenerator.cs +++ b/Funcky.DiscriminatedUnion.SourceGeneration/DiscriminatedUnionGenerator.cs @@ -1,4 +1,5 @@ using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using static Funcky.DiscriminatedUnion.SourceGeneration.Emitter; @@ -32,8 +33,7 @@ private static void AddSource(SourceProductionContext context, ImmutableArray $"global::System.Collections.Generic.IReadOnlyList<{discriminatedUnion.Type.Identifier}.{v.LocalTypeName}> {v.ParameterName[..1].ToUpper()}{v.ParameterName[1..]}", + ", "); + + writer.WriteLineInterpolated($"public static ({namedResultPartitions}) Partition(this global::System.Collections.Generic.IEnumerable<{discriminatedUnion.Type.Identifier}> source)"); + writer.OpenScope(); + + WritePartitioningIntoLists(discriminatedUnion, writer); + + writer.WriteLineInterpolated($"return ({discriminatedUnion.Variants.JoinToInterpolation(v => $"{v.ParameterName}Items.AsReadOnly()", ", ")});"); + } + + private static void WritePartitionWithResultSelector(DiscriminatedUnion discriminatedUnion, IndentedTextWriter writer) + { + using var methodScope = writer.AutoCloseScopes(); + + writer.WriteInterpolated($"public static TResult Partition<{discriminatedUnion.MatchResultTypeName}>(this global::System.Collections.Generic.IEnumerable<{discriminatedUnion.Type.Identifier}> source, global::System.Func<"); + + foreach (var variant in discriminatedUnion.Variants) + { + writer.WriteInterpolated($"global::System.Collections.Generic.IReadOnlyList<{discriminatedUnion.Type.Identifier}.{variant.LocalTypeName}>, "); + } + + writer.WriteLineInterpolated($"{discriminatedUnion.MatchResultTypeName}> resultSelector)"); + writer.OpenScope(); + + WritePartitioningIntoLists(discriminatedUnion, writer); + + writer.WriteLineInterpolated($"return resultSelector({discriminatedUnion.Variants.JoinToInterpolation(v => $"{v.ParameterName}Items.AsReadOnly()", ", ")});"); + } + + private static void WritePartitioningIntoLists(DiscriminatedUnion discriminatedUnion, IndentedTextWriter writer) + { + foreach (var variant in discriminatedUnion.Variants) + { + writer.WriteLineInterpolated($"var {variant.ParameterName}Items = new global::System.Collections.Generic.List<{discriminatedUnion.Type.Identifier}.{variant.LocalTypeName}>();"); + } + + using (writer.AutoCloseScopes()) + { + writer.WriteLine("foreach (var item in source)"); + writer.OpenScope(); + writer.WriteLineInterpolated($"item.Switch({discriminatedUnion.Variants.JoinToInterpolation(v => $"{v.ParameterName}: {v.ParameterName}Items.Add", ", ")});"); + } + } + private static void WriteNamespace(IndentedTextWriter writer, DiscriminatedUnion discriminatedUnion) { if (!string.IsNullOrEmpty(discriminatedUnion.Namespace)) { - writer.WriteLine($"namespace {discriminatedUnion.Namespace}"); + writer.WriteLineInterpolated($"namespace {discriminatedUnion.Namespace}"); writer.OpenScope(); } } @@ -50,7 +127,7 @@ private static void WriteParentTypes(IndentedTextWriter writer, IEnumerable {FormatIdentifier(variant.ParameterName)}(this);"); + WriteGeneratedMethod(writer, $"{discriminatedUnion.GeneratedMethodOrClassVisibility} override {FormatMatchMethodDeclaration(discriminatedUnion.MatchResultTypeName, discriminatedUnion.Variants)} => {FormatIdentifier(variant.ParameterName)}(this);"); writer.WriteLine(); - WriteGeneratedMethod(writer, $"{discriminatedUnion.MethodVisibility} override {FormatSwitchMethodDeclaration(discriminatedUnion.Variants)} => {FormatIdentifier(variant.ParameterName)}(this);"); + WriteGeneratedMethod(writer, $"{discriminatedUnion.GeneratedMethodOrClassVisibility} override {FormatSwitchMethodDeclaration(discriminatedUnion.Variants)} => {FormatIdentifier(variant.ParameterName)}(this);"); } } - private static void WriteGeneratedMethod(IndentedTextWriter writer, string method) + private static void WriteGeneratedMethod(IndentedTextWriter writer, CollectingInterpolatedStringHandler method) { writer.WriteLine(GeneratedCodeAttributeSource); - writer.WriteLine(method); + writer.WriteLineInterpolated(method); } private static void WriteJsonDerivedTypeAttributes(IndentedTextWriter writer, DiscriminatedUnion discriminatedUnion) @@ -90,26 +167,26 @@ private static void WriteJsonDerivedTypeAttribute(IndentedTextWriter writer, Dis { if (variant.GenerateJsonDerivedTypeAttribute) { - writer.WriteLine($"[global::System.Text.Json.Serialization.JsonDerivedType(typeof({variant.TypeOfTypeName}), {SyntaxFactory.Literal(variant.JsonDerivedTypeDiscriminator)})]"); + writer.WriteLineInterpolated($"[global::System.Text.Json.Serialization.JsonDerivedType(typeof({variant.TypeOfTypeName}), {SyntaxFactory.Literal(variant.JsonDerivedTypeDiscriminator)})]"); } } - private static string FormatMatchMethodDeclaration(string genericTypeName, IEnumerable variants) - => $"{genericTypeName} Match<{genericTypeName}>({string.Join(", ", variants.Select(variant => $"global::System.Func<{variant.LocalTypeName}, {genericTypeName}> {FormatIdentifier(variant.ParameterName)}"))})"; + private static CollectingInterpolatedStringHandler FormatMatchMethodDeclaration(string genericTypeName, IEnumerable variants) + => $"{genericTypeName} Match<{genericTypeName}>({variants.JoinToInterpolation(v => $"global::System.Func<{v.LocalTypeName}, {genericTypeName}> {FormatIdentifier(v.ParameterName)}", ", ")})"; - private static string FormatSwitchMethodDeclaration(IEnumerable variants) - => $"void Switch({string.Join(", ", variants.Select(variant => $"global::System.Action<{variant.LocalTypeName}> {FormatIdentifier(variant.ParameterName)}"))})"; + private static CollectingInterpolatedStringHandler FormatSwitchMethodDeclaration(IEnumerable variants) + => $"void Switch({variants.JoinToInterpolation(v => $"global::System.Action<{v.LocalTypeName}> {FormatIdentifier(v.ParameterName)}", ", ")})"; - private static string FormatPartialTypeDeclaration(TypeDeclarationSyntax typeDeclaration) + private static CollectingInterpolatedStringHandler FormatPartialTypeDeclaration(TypeDeclarationSyntax typeDeclaration) => typeDeclaration is RecordDeclarationSyntax recordDeclaration ? CombineTokens("partial", typeDeclaration.Keyword, recordDeclaration.ClassOrStructKeyword, typeDeclaration.Identifier.ToString() + typeDeclaration.TypeParameterList, typeDeclaration.ConstraintClauses) : CombineTokens("partial", typeDeclaration.Keyword, typeDeclaration.Identifier.ToString() + typeDeclaration.TypeParameterList, typeDeclaration.ConstraintClauses); - private static string CombineTokens(params object[] tokens) - => string.Join(" ", tokens.Select(t => t.ToString()).Where(t => !string.IsNullOrEmpty(t))); + private static CollectingInterpolatedStringHandler CombineTokens(params object[] tokens) + => tokens.Select(t => t.ToString()).Where(t => !string.IsNullOrEmpty(t)).JoinToInterpolation(" "); - private static string FormatIdentifier(string identifier) - => IsIdentifier(identifier) ? '@' + identifier : identifier; + private static CollectingInterpolatedStringHandler FormatIdentifier(string identifier) + => $"{(IsIdentifier(identifier) ? "@" : string.Empty)}{identifier}"; private static bool IsIdentifier(string identifier) => SyntaxFacts.GetKeywordKind(identifier) != SyntaxKind.None; diff --git a/Funcky.DiscriminatedUnion.SourceGeneration/Funcky.DiscriminatedUnion.SourceGeneration.csproj b/Funcky.DiscriminatedUnion.SourceGeneration/Funcky.DiscriminatedUnion.SourceGeneration.csproj index 16fd170..67b24d1 100644 --- a/Funcky.DiscriminatedUnion.SourceGeneration/Funcky.DiscriminatedUnion.SourceGeneration.csproj +++ b/Funcky.DiscriminatedUnion.SourceGeneration/Funcky.DiscriminatedUnion.SourceGeneration.csproj @@ -6,9 +6,10 @@ enable enable 11.0 + true - 1.1.0 + 1.2.0 Funcky.DiscriminatedUnion Polyadic MIT OR Apache-2.0 @@ -40,6 +41,6 @@ - + diff --git a/Funcky.DiscriminatedUnion.SourceGeneration/IndentedTextWriterExtensions.cs b/Funcky.DiscriminatedUnion.SourceGeneration/IndentedTextWriterExtensions.cs index b5e13a0..9c62a84 100644 --- a/Funcky.DiscriminatedUnion.SourceGeneration/IndentedTextWriterExtensions.cs +++ b/Funcky.DiscriminatedUnion.SourceGeneration/IndentedTextWriterExtensions.cs @@ -22,6 +22,20 @@ public static void OpenScope(this IndentedTextWriter writer) writer.Indent++; } + public static void WriteInterpolated(this IndentedTextWriter writer, CollectingInterpolatedStringHandler value) + { + foreach (var item in value.GetItems()) + { + writer.Write(item?.ToString()); + } + } + + public static void WriteLineInterpolated(this IndentedTextWriter writer, CollectingInterpolatedStringHandler value) + { + writer.WriteInterpolated(value); + writer.WriteLine(); + } + private static void CloseScope(this IndentedTextWriter writer) { writer.Indent--; diff --git a/Funcky.DiscriminatedUnion.SourceGeneration/Parser.cs b/Funcky.DiscriminatedUnion.SourceGeneration/Parser.cs index f236948..1f0dea5 100644 --- a/Funcky.DiscriminatedUnion.SourceGeneration/Parser.cs +++ b/Funcky.DiscriminatedUnion.SourceGeneration/Parser.cs @@ -30,7 +30,7 @@ public static bool IsSyntaxTargetForGeneration(SyntaxNode node) return null; } - var (nonExhaustive, flatten, matchResultType) = ParseAttribute(typeSymbol); + var (nonExhaustive, flatten, matchResultType, generatePartitionExtension) = ParseAttribute(typeSymbol); var isVariant = flatten ? IsVariantOfDiscriminatedUnionFlattened(typeSymbol, semanticModel) : IsVariantOfDiscriminatedUnion(typeSymbol, semanticModel); return new DiscriminatedUnion( @@ -38,7 +38,8 @@ public static bool IsSyntaxTargetForGeneration(SyntaxNode node) ParentTypes: typeDeclaration.Ancestors().OfType().ToList(), Namespace: FormatNamespace(typeSymbol), MatchResultTypeName: matchResultType ?? "TResult", - MethodVisibility: nonExhaustive ? "internal" : "public", + GeneratedMethodOrClassVisibility: nonExhaustive ? "internal" : "public", + GeneratePartitionExtension: generatePartitionExtension, Variants: GetVariantTypeDeclarations(typeDeclaration, isVariant) .Select(GetDiscriminatedUnionVariant(typeDeclaration, semanticModel, GenerateJsonDerivedTypeAttribute(typeSymbol))) .ToList()); @@ -50,7 +51,8 @@ private static DiscriminatedUnionAttributeData ParseAttribute(ITypeSymbol type) var nonExhaustive = attribute.GetNamedArgumentOrDefault(AttributeProperties.NonExhaustive); var flatten = attribute.GetNamedArgumentOrDefault(AttributeProperties.Flatten); var matchResultType = attribute.GetNamedArgumentOrDefault(AttributeProperties.MatchResultTypeName); - return new DiscriminatedUnionAttributeData(nonExhaustive, flatten, matchResultType); + var generatePartitionExtension = attribute.GetNamedArgumentOrDefault(AttributeProperties.GeneratePartitionExtension); + return new DiscriminatedUnionAttributeData(nonExhaustive, flatten, matchResultType, generatePartitionExtension); } private static string? FormatNamespace(INamedTypeSymbol typeSymbol) @@ -119,7 +121,11 @@ private static Func IsDiscriminatedUnionAttribute(Generat => context.SemanticModel.GetSymbolInfo(attribute, cancellationToken).Symbol is IMethodSymbol attributeSymbol && attributeSymbol.ContainingType.ToDisplayString() == AttributeFullName; - private sealed record DiscriminatedUnionAttributeData(bool NonExhaustive, bool Flatten, string? MatchResultType); + private sealed record DiscriminatedUnionAttributeData( + bool NonExhaustive, + bool Flatten, + string? MatchResultType, + bool GeneratePartitionExtension); private sealed class VariantCollectingVisitor : CSharpSyntaxWalker { diff --git a/Funcky.DiscriminatedUnion.SourceGeneration/SourceCodeSnippets.cs b/Funcky.DiscriminatedUnion.SourceGeneration/SourceCodeSnippets.cs index b8839c6..de15688 100644 --- a/Funcky.DiscriminatedUnion.SourceGeneration/SourceCodeSnippets.cs +++ b/Funcky.DiscriminatedUnion.SourceGeneration/SourceCodeSnippets.cs @@ -29,6 +29,9 @@ internal sealed class DiscriminatedUnionAttribute : global::System.Attribute /// Generates exhaustive Match and Switch methods for the entire type hierarchy. public bool {{AttributeProperties.Flatten}} { get; set; } + /// If a specialized partition extension method for IEnumerable should be generated. Defaults to . + public bool {{AttributeProperties.GeneratePartitionExtension}} { get; set; } + /// Customized the generic type name used for the result in the generated Match methods. Defaults to TResult. public string? {{AttributeProperties.MatchResultTypeName}} { get; set; } } @@ -46,6 +49,7 @@ public static class AttributeProperties { public const string NonExhaustive = "NonExhaustive"; public const string Flatten = "Flatten"; + public const string GeneratePartitionExtension = "GeneratePartitionExtension"; public const string MatchResultTypeName = "MatchResultTypeName"; } } diff --git a/Funcky.DiscriminatedUnion.Test/Funcky.DiscriminatedUnion.Test.csproj b/Funcky.DiscriminatedUnion.Test/Funcky.DiscriminatedUnion.Test.csproj index 592d660..211f567 100644 --- a/Funcky.DiscriminatedUnion.Test/Funcky.DiscriminatedUnion.Test.csproj +++ b/Funcky.DiscriminatedUnion.Test/Funcky.DiscriminatedUnion.Test.csproj @@ -1,9 +1,9 @@ - net7.0 + net8.0 enable enable - 11.0 + 12.0 false diff --git a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=DeeplyNestedUnion.00.verified.cs b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=DeeplyNestedUnion.00.verified.cs index 37cbda7..653d24f 100644 --- a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=DeeplyNestedUnion.00.verified.cs +++ b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=DeeplyNestedUnion.00.verified.cs @@ -14,6 +14,9 @@ internal sealed class DiscriminatedUnionAttribute : global::System.Attribute /// Generates exhaustive Match and Switch methods for the entire type hierarchy. public bool Flatten { get; set; } + /// If a specialized partition extension method for IEnumerable should be generated. Defaults to . + public bool GeneratePartitionExtension { get; set; } + /// Customized the generic type name used for the result in the generated Match methods. Defaults to TResult. public string? MatchResultTypeName { get; set; } } diff --git a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=DeeplyNestedUnion.01.verified.cs b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=DeeplyNestedUnion.01.verified.cs index 4e75c79..3d937ab 100644 --- a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=DeeplyNestedUnion.01.verified.cs +++ b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=DeeplyNestedUnion.01.verified.cs @@ -14,18 +14,18 @@ partial class StaticClass { partial record NestedUnion { - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public abstract TResult Match(global::System.Func variant); - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public abstract void Switch(global::System.Action variant); partial record Variant { - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override TResult Match(global::System.Func variant) => variant(this); - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override void Switch(global::System.Action variant) => variant(this); } } diff --git a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=EmptyUnion.00.verified.cs b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=EmptyUnion.00.verified.cs index 37cbda7..653d24f 100644 --- a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=EmptyUnion.00.verified.cs +++ b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=EmptyUnion.00.verified.cs @@ -14,6 +14,9 @@ internal sealed class DiscriminatedUnionAttribute : global::System.Attribute /// Generates exhaustive Match and Switch methods for the entire type hierarchy. public bool Flatten { get; set; } + /// If a specialized partition extension method for IEnumerable should be generated. Defaults to . + public bool GeneratePartitionExtension { get; set; } + /// Customized the generic type name used for the result in the generated Match methods. Defaults to TResult. public string? MatchResultTypeName { get; set; } } diff --git a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=EmptyUnion.01.verified.cs b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=EmptyUnion.01.verified.cs index ae456ec..6122bc4 100644 --- a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=EmptyUnion.01.verified.cs +++ b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=EmptyUnion.01.verified.cs @@ -4,9 +4,9 @@ partial record EmptyUnion { - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public abstract TResult Match(); - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public abstract void Switch(); } diff --git a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=FlattenedNestedUnionWithPartition.00.verified.cs b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=FlattenedNestedUnionWithPartition.00.verified.cs new file mode 100644 index 0000000..653d24f --- /dev/null +++ b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=FlattenedNestedUnionWithPartition.00.verified.cs @@ -0,0 +1,23 @@ +//HintName: DiscriminatedUnionAttribute.g.cs +// +#nullable enable + +namespace Funcky +{ + [global::System.Diagnostics.Conditional("Funcky_DiscriminatedUnion")] + [global::System.AttributeUsage(global::System.AttributeTargets.Class)] + internal sealed class DiscriminatedUnionAttribute : global::System.Attribute + { + /// Allow only consumers in the same assembly to use the exhaustive Match and Switch methods. + public bool NonExhaustive { get; set; } + + /// Generates exhaustive Match and Switch methods for the entire type hierarchy. + public bool Flatten { get; set; } + + /// If a specialized partition extension method for IEnumerable should be generated. Defaults to . + public bool GeneratePartitionExtension { get; set; } + + /// Customized the generic type name used for the result in the generated Match methods. Defaults to TResult. + public string? MatchResultTypeName { get; set; } + } +} diff --git a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=FlattenedNestedUnionWithPartition.01.verified.cs b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=FlattenedNestedUnionWithPartition.01.verified.cs new file mode 100644 index 0000000..9c3bf92 --- /dev/null +++ b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=FlattenedNestedUnionWithPartition.01.verified.cs @@ -0,0 +1,65 @@ +//HintName: DiscriminatedUnionGenerator.g.cs +// +#nullable enable + +namespace Funcky.DiscriminatedUnion.Test +{ + partial record FlattenedNestedUnionWithPartition + { + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] + public abstract TResult Match(global::System.Func keyword, global::System.Func integer); + + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] + public abstract void Switch(global::System.Action keyword, global::System.Action integer); + + partial record Keyword + { + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] + public override TResult Match(global::System.Func keyword, global::System.Func integer) => keyword(this); + + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] + public override void Switch(global::System.Action keyword, global::System.Action integer) => keyword(this); + } + + partial record Literal + { + partial record Number + { + partial record Integer + { + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] + public override TResult Match(global::System.Func keyword, global::System.Func integer) => integer(this); + + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] + public override void Switch(global::System.Action keyword, global::System.Action integer) => integer(this); + } + } + } + } + + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] + public static partial class FlattenedNestedUnionWithPartitionEnumerableExtensions + { + public static (global::System.Collections.Generic.IReadOnlyList Keyword, global::System.Collections.Generic.IReadOnlyList Integer) Partition(this global::System.Collections.Generic.IEnumerable source) + { + var keywordItems = new global::System.Collections.Generic.List(); + var integerItems = new global::System.Collections.Generic.List(); + foreach (var item in source) + { + item.Switch(keyword: keywordItems.Add, integer: integerItems.Add); + } + return (keywordItems.AsReadOnly(), integerItems.AsReadOnly()); + } + + public static TResult Partition(this global::System.Collections.Generic.IEnumerable source, global::System.Func, global::System.Collections.Generic.IReadOnlyList, TResult> resultSelector) + { + var keywordItems = new global::System.Collections.Generic.List(); + var integerItems = new global::System.Collections.Generic.List(); + foreach (var item in source) + { + item.Switch(keyword: keywordItems.Add, integer: integerItems.Add); + } + return resultSelector(keywordItems.AsReadOnly(), integerItems.AsReadOnly()); + } + } +} diff --git a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=FlattenedUnionWithPartition.00.verified.cs b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=FlattenedUnionWithPartition.00.verified.cs new file mode 100644 index 0000000..653d24f --- /dev/null +++ b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=FlattenedUnionWithPartition.00.verified.cs @@ -0,0 +1,23 @@ +//HintName: DiscriminatedUnionAttribute.g.cs +// +#nullable enable + +namespace Funcky +{ + [global::System.Diagnostics.Conditional("Funcky_DiscriminatedUnion")] + [global::System.AttributeUsage(global::System.AttributeTargets.Class)] + internal sealed class DiscriminatedUnionAttribute : global::System.Attribute + { + /// Allow only consumers in the same assembly to use the exhaustive Match and Switch methods. + public bool NonExhaustive { get; set; } + + /// Generates exhaustive Match and Switch methods for the entire type hierarchy. + public bool Flatten { get; set; } + + /// If a specialized partition extension method for IEnumerable should be generated. Defaults to . + public bool GeneratePartitionExtension { get; set; } + + /// Customized the generic type name used for the result in the generated Match methods. Defaults to TResult. + public string? MatchResultTypeName { get; set; } + } +} diff --git a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=FlattenedUnionWithPartition.01.verified.cs b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=FlattenedUnionWithPartition.01.verified.cs new file mode 100644 index 0000000..6bc51e7 --- /dev/null +++ b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=FlattenedUnionWithPartition.01.verified.cs @@ -0,0 +1,59 @@ +//HintName: DiscriminatedUnionGenerator.g.cs +// +#nullable enable + +namespace Funcky.DiscriminatedUnion.Test +{ + partial record FlattenedUnionWithPartition + { + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] + public abstract TResult Match(global::System.Func keyword, global::System.Func integer); + + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] + public abstract void Switch(global::System.Action keyword, global::System.Action integer); + + partial record Keyword + { + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] + public override TResult Match(global::System.Func keyword, global::System.Func integer) => keyword(this); + + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] + public override void Switch(global::System.Action keyword, global::System.Action integer) => keyword(this); + } + + partial record Integer + { + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] + public override TResult Match(global::System.Func keyword, global::System.Func integer) => integer(this); + + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] + public override void Switch(global::System.Action keyword, global::System.Action integer) => integer(this); + } + } + + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] + public static partial class FlattenedUnionWithPartitionEnumerableExtensions + { + public static (global::System.Collections.Generic.IReadOnlyList Keyword, global::System.Collections.Generic.IReadOnlyList Integer) Partition(this global::System.Collections.Generic.IEnumerable source) + { + var keywordItems = new global::System.Collections.Generic.List(); + var integerItems = new global::System.Collections.Generic.List(); + foreach (var item in source) + { + item.Switch(keyword: keywordItems.Add, integer: integerItems.Add); + } + return (keywordItems.AsReadOnly(), integerItems.AsReadOnly()); + } + + public static TResult Partition(this global::System.Collections.Generic.IEnumerable source, global::System.Func, global::System.Collections.Generic.IReadOnlyList, TResult> resultSelector) + { + var keywordItems = new global::System.Collections.Generic.List(); + var integerItems = new global::System.Collections.Generic.List(); + foreach (var item in source) + { + item.Switch(keyword: keywordItems.Add, integer: integerItems.Add); + } + return resultSelector(keywordItems.AsReadOnly(), integerItems.AsReadOnly()); + } + } +} diff --git a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=GenericUnion.00.verified.cs b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=GenericUnion.00.verified.cs index 37cbda7..653d24f 100644 --- a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=GenericUnion.00.verified.cs +++ b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=GenericUnion.00.verified.cs @@ -14,6 +14,9 @@ internal sealed class DiscriminatedUnionAttribute : global::System.Attribute /// Generates exhaustive Match and Switch methods for the entire type hierarchy. public bool Flatten { get; set; } + /// If a specialized partition extension method for IEnumerable should be generated. Defaults to . + public bool GeneratePartitionExtension { get; set; } + /// Customized the generic type name used for the result in the generated Match methods. Defaults to TResult. public string? MatchResultTypeName { get; set; } } diff --git a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=GenericUnion.01.verified.cs b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=GenericUnion.01.verified.cs index 165fd82..12e0220 100644 --- a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=GenericUnion.01.verified.cs +++ b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=GenericUnion.01.verified.cs @@ -6,27 +6,27 @@ namespace Funcky.DiscriminatedUnion.Test { partial record Result where T : notnull { - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] internal abstract TResult Match(global::System.Func ok, global::System.Func error); - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] internal abstract void Switch(global::System.Action ok, global::System.Action error); partial record Ok { - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] internal override TResult Match(global::System.Func ok, global::System.Func error) => ok(this); - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] internal override void Switch(global::System.Action ok, global::System.Action error) => ok(this); } partial record Error { - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] internal override TResult Match(global::System.Func ok, global::System.Func error) => error(this); - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] internal override void Switch(global::System.Action ok, global::System.Action error) => error(this); } } diff --git a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=JsonPolymorphic.00.verified.cs b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=JsonPolymorphic.00.verified.cs index 37cbda7..653d24f 100644 --- a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=JsonPolymorphic.00.verified.cs +++ b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=JsonPolymorphic.00.verified.cs @@ -14,6 +14,9 @@ internal sealed class DiscriminatedUnionAttribute : global::System.Attribute /// Generates exhaustive Match and Switch methods for the entire type hierarchy. public bool Flatten { get; set; } + /// If a specialized partition extension method for IEnumerable should be generated. Defaults to . + public bool GeneratePartitionExtension { get; set; } + /// Customized the generic type name used for the result in the generated Match methods. Defaults to TResult. public string? MatchResultTypeName { get; set; } } diff --git a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=JsonPolymorphic.01.verified.cs b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=JsonPolymorphic.01.verified.cs index e82aa26..6138e1e 100644 --- a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=JsonPolymorphic.01.verified.cs +++ b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=JsonPolymorphic.01.verified.cs @@ -6,27 +6,27 @@ [global::System.Text.Json.Serialization.JsonDerivedType(typeof(global::Result.Error), "Error")] partial record Result { - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public abstract TResult Match(global::System.Func ok, global::System.Func error); - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public abstract void Switch(global::System.Action ok, global::System.Action error); partial record Ok { - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override TResult Match(global::System.Func ok, global::System.Func error) => ok(this); - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override void Switch(global::System.Action ok, global::System.Action error) => ok(this); } partial record Error { - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override TResult Match(global::System.Func ok, global::System.Func error) => error(this); - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override void Switch(global::System.Action ok, global::System.Action error) => error(this); } } @@ -39,27 +39,27 @@ partial class Nesting2 [global::System.Text.Json.Serialization.JsonDerivedType(typeof(global::Nesting1<, , >.Nesting2<>.Result<>.Error), "Error")] partial record Result { - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public abstract TResult Match(global::System.Func ok, global::System.Func error); - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public abstract void Switch(global::System.Action ok, global::System.Action error); partial record Ok { - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override TResult Match(global::System.Func ok, global::System.Func error) => ok(this); - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override void Switch(global::System.Action ok, global::System.Action error) => ok(this); } partial record Error { - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override TResult Match(global::System.Func ok, global::System.Func error) => error(this); - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override void Switch(global::System.Action ok, global::System.Action error) => error(this); } } @@ -69,36 +69,36 @@ partial record Error [global::System.Text.Json.Serialization.JsonDerivedType(typeof(global::Shape.EquilateralTriangle), "EquilateralTriangle")] partial record Shape { - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public abstract TResult Match(global::System.Func rectangle, global::System.Func circle, global::System.Func equilateralTriangle); - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public abstract void Switch(global::System.Action rectangle, global::System.Action circle, global::System.Action equilateralTriangle); partial record Rectangle { - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override TResult Match(global::System.Func rectangle, global::System.Func circle, global::System.Func equilateralTriangle) => rectangle(this); - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override void Switch(global::System.Action rectangle, global::System.Action circle, global::System.Action equilateralTriangle) => rectangle(this); } partial record Circle { - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override TResult Match(global::System.Func rectangle, global::System.Func circle, global::System.Func equilateralTriangle) => circle(this); - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override void Switch(global::System.Action rectangle, global::System.Action circle, global::System.Action equilateralTriangle) => circle(this); } partial record EquilateralTriangle { - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override TResult Match(global::System.Func rectangle, global::System.Func circle, global::System.Func equilateralTriangle) => equilateralTriangle(this); - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override void Switch(global::System.Action rectangle, global::System.Action circle, global::System.Action equilateralTriangle) => equilateralTriangle(this); } } diff --git a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=KeywordsAsParameterNames.00.verified.cs b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=KeywordsAsParameterNames.00.verified.cs index 37cbda7..653d24f 100644 --- a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=KeywordsAsParameterNames.00.verified.cs +++ b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=KeywordsAsParameterNames.00.verified.cs @@ -14,6 +14,9 @@ internal sealed class DiscriminatedUnionAttribute : global::System.Attribute /// Generates exhaustive Match and Switch methods for the entire type hierarchy. public bool Flatten { get; set; } + /// If a specialized partition extension method for IEnumerable should be generated. Defaults to . + public bool GeneratePartitionExtension { get; set; } + /// Customized the generic type name used for the result in the generated Match methods. Defaults to TResult. public string? MatchResultTypeName { get; set; } } diff --git a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=KeywordsAsParameterNames.01.verified.cs b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=KeywordsAsParameterNames.01.verified.cs index 2efd68c..836a0b7 100644 --- a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=KeywordsAsParameterNames.01.verified.cs +++ b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=KeywordsAsParameterNames.01.verified.cs @@ -6,27 +6,27 @@ namespace Funcky.DiscriminatedUnion.Test { partial record Bool { - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public abstract TResult Match(global::System.Func @true, global::System.Func @false); - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public abstract void Switch(global::System.Action @true, global::System.Action @false); partial record True { - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override TResult Match(global::System.Func @true, global::System.Func @false) => @true(this); - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override void Switch(global::System.Action @true, global::System.Action @false) => @true(this); } partial record False { - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override TResult Match(global::System.Func @true, global::System.Func @false) => @false(this); - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override void Switch(global::System.Action @true, global::System.Action @false) => @false(this); } } diff --git a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=LogicallyAndSyntacticallyNestedUnion.00.verified.cs b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=LogicallyAndSyntacticallyNestedUnion.00.verified.cs index 37cbda7..653d24f 100644 --- a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=LogicallyAndSyntacticallyNestedUnion.00.verified.cs +++ b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=LogicallyAndSyntacticallyNestedUnion.00.verified.cs @@ -14,6 +14,9 @@ internal sealed class DiscriminatedUnionAttribute : global::System.Attribute /// Generates exhaustive Match and Switch methods for the entire type hierarchy. public bool Flatten { get; set; } + /// If a specialized partition extension method for IEnumerable should be generated. Defaults to . + public bool GeneratePartitionExtension { get; set; } + /// Customized the generic type name used for the result in the generated Match methods. Defaults to TResult. public string? MatchResultTypeName { get; set; } } diff --git a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=LogicallyAndSyntacticallyNestedUnion.01.verified.cs b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=LogicallyAndSyntacticallyNestedUnion.01.verified.cs index 7fb5f33..c5e5703 100644 --- a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=LogicallyAndSyntacticallyNestedUnion.01.verified.cs +++ b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=LogicallyAndSyntacticallyNestedUnion.01.verified.cs @@ -6,27 +6,27 @@ namespace Funcky.DiscriminatedUnion.Test { partial record SyntaxNode { - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public abstract TResult Match(global::System.Func keyword, global::System.Func literal); - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public abstract void Switch(global::System.Action keyword, global::System.Action literal); partial record Keyword { - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override TResult Match(global::System.Func keyword, global::System.Func literal) => keyword(this); - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override void Switch(global::System.Action keyword, global::System.Action literal) => keyword(this); } partial record Literal { - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override TResult Match(global::System.Func keyword, global::System.Func literal) => literal(this); - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override void Switch(global::System.Action keyword, global::System.Action literal) => literal(this); } } @@ -38,27 +38,27 @@ partial record SyntaxNode { partial record Literal { - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public abstract TResult Match(global::System.Func integer, global::System.Func @string); - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public abstract void Switch(global::System.Action integer, global::System.Action @string); partial record Integer { - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override TResult Match(global::System.Func integer, global::System.Func @string) => integer(this); - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override void Switch(global::System.Action integer, global::System.Action @string) => integer(this); } partial record String { - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override TResult Match(global::System.Func integer, global::System.Func @string) => @string(this); - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override void Switch(global::System.Action integer, global::System.Action @string) => @string(this); } } diff --git a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=LogicallyAndSyntacticallyNestedUnionWithFlatten.00.verified.cs b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=LogicallyAndSyntacticallyNestedUnionWithFlatten.00.verified.cs index 37cbda7..653d24f 100644 --- a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=LogicallyAndSyntacticallyNestedUnionWithFlatten.00.verified.cs +++ b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=LogicallyAndSyntacticallyNestedUnionWithFlatten.00.verified.cs @@ -14,6 +14,9 @@ internal sealed class DiscriminatedUnionAttribute : global::System.Attribute /// Generates exhaustive Match and Switch methods for the entire type hierarchy. public bool Flatten { get; set; } + /// If a specialized partition extension method for IEnumerable should be generated. Defaults to . + public bool GeneratePartitionExtension { get; set; } + /// Customized the generic type name used for the result in the generated Match methods. Defaults to TResult. public string? MatchResultTypeName { get; set; } } diff --git a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=LogicallyAndSyntacticallyNestedUnionWithFlatten.01.verified.cs b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=LogicallyAndSyntacticallyNestedUnionWithFlatten.01.verified.cs index 2abe511..6d4ca15 100644 --- a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=LogicallyAndSyntacticallyNestedUnionWithFlatten.01.verified.cs +++ b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=LogicallyAndSyntacticallyNestedUnionWithFlatten.01.verified.cs @@ -6,18 +6,18 @@ namespace Funcky.DiscriminatedUnion.Test { partial record SyntaxNodeFlattened { - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public abstract TResult Match(global::System.Func keyword, global::System.Func integer); - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public abstract void Switch(global::System.Action keyword, global::System.Action integer); partial record Keyword { - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override TResult Match(global::System.Func keyword, global::System.Func integer) => keyword(this); - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override void Switch(global::System.Action keyword, global::System.Action integer) => keyword(this); } @@ -27,10 +27,10 @@ partial record Number { partial record Integer { - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override TResult Match(global::System.Func keyword, global::System.Func integer) => integer(this); - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override void Switch(global::System.Action keyword, global::System.Action integer) => integer(this); } } diff --git a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=LogicallyNestedUnionWithFlatten.00.verified.cs b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=LogicallyNestedUnionWithFlatten.00.verified.cs index 37cbda7..653d24f 100644 --- a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=LogicallyNestedUnionWithFlatten.00.verified.cs +++ b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=LogicallyNestedUnionWithFlatten.00.verified.cs @@ -14,6 +14,9 @@ internal sealed class DiscriminatedUnionAttribute : global::System.Attribute /// Generates exhaustive Match and Switch methods for the entire type hierarchy. public bool Flatten { get; set; } + /// If a specialized partition extension method for IEnumerable should be generated. Defaults to . + public bool GeneratePartitionExtension { get; set; } + /// Customized the generic type name used for the result in the generated Match methods. Defaults to TResult. public string? MatchResultTypeName { get; set; } } diff --git a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=LogicallyNestedUnionWithFlatten.01.verified.cs b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=LogicallyNestedUnionWithFlatten.01.verified.cs index dbe5dfe..0ea2132 100644 --- a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=LogicallyNestedUnionWithFlatten.01.verified.cs +++ b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=LogicallyNestedUnionWithFlatten.01.verified.cs @@ -6,27 +6,27 @@ namespace Funcky.DiscriminatedUnion.Test { partial record SyntaxNodeFlattened { - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public abstract TResult Match(global::System.Func keyword, global::System.Func integer); - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public abstract void Switch(global::System.Action keyword, global::System.Action integer); partial record Keyword { - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override TResult Match(global::System.Func keyword, global::System.Func integer) => keyword(this); - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override void Switch(global::System.Action keyword, global::System.Action integer) => keyword(this); } partial record Integer { - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override TResult Match(global::System.Func keyword, global::System.Func integer) => integer(this); - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override void Switch(global::System.Action keyword, global::System.Action integer) => integer(this); } } diff --git a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=NonExhaustive.00.verified.cs b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=NonExhaustive.00.verified.cs index 37cbda7..653d24f 100644 --- a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=NonExhaustive.00.verified.cs +++ b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=NonExhaustive.00.verified.cs @@ -14,6 +14,9 @@ internal sealed class DiscriminatedUnionAttribute : global::System.Attribute /// Generates exhaustive Match and Switch methods for the entire type hierarchy. public bool Flatten { get; set; } + /// If a specialized partition extension method for IEnumerable should be generated. Defaults to . + public bool GeneratePartitionExtension { get; set; } + /// Customized the generic type name used for the result in the generated Match methods. Defaults to TResult. public string? MatchResultTypeName { get; set; } } diff --git a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=NonExhaustive.01.verified.cs b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=NonExhaustive.01.verified.cs index 165fd82..12e0220 100644 --- a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=NonExhaustive.01.verified.cs +++ b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=NonExhaustive.01.verified.cs @@ -6,27 +6,27 @@ namespace Funcky.DiscriminatedUnion.Test { partial record Result where T : notnull { - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] internal abstract TResult Match(global::System.Func ok, global::System.Func error); - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] internal abstract void Switch(global::System.Action ok, global::System.Action error); partial record Ok { - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] internal override TResult Match(global::System.Func ok, global::System.Func error) => ok(this); - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] internal override void Switch(global::System.Action ok, global::System.Action error) => ok(this); } partial record Error { - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] internal override TResult Match(global::System.Func ok, global::System.Func error) => error(this); - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] internal override void Switch(global::System.Action ok, global::System.Action error) => error(this); } } diff --git a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=UnionNestedInMultipleNamespaces.00.verified.cs b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=UnionNestedInMultipleNamespaces.00.verified.cs index 37cbda7..653d24f 100644 --- a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=UnionNestedInMultipleNamespaces.00.verified.cs +++ b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=UnionNestedInMultipleNamespaces.00.verified.cs @@ -14,6 +14,9 @@ internal sealed class DiscriminatedUnionAttribute : global::System.Attribute /// Generates exhaustive Match and Switch methods for the entire type hierarchy. public bool Flatten { get; set; } + /// If a specialized partition extension method for IEnumerable should be generated. Defaults to . + public bool GeneratePartitionExtension { get; set; } + /// Customized the generic type name used for the result in the generated Match methods. Defaults to TResult. public string? MatchResultTypeName { get; set; } } diff --git a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=UnionNestedInMultipleNamespaces.01.verified.cs b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=UnionNestedInMultipleNamespaces.01.verified.cs index b8193a6..31b732f 100644 --- a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=UnionNestedInMultipleNamespaces.01.verified.cs +++ b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=UnionNestedInMultipleNamespaces.01.verified.cs @@ -6,18 +6,18 @@ namespace Foo.Bar { partial record NestedUnion { - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public abstract TResult Match(global::System.Func variant); - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public abstract void Switch(global::System.Action variant); partial record Variant { - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override TResult Match(global::System.Func variant) => variant(this); - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override void Switch(global::System.Action variant) => variant(this); } } diff --git a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=UnionWithConflictingResultTypeName.00.verified.cs b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=UnionWithConflictingResultTypeName.00.verified.cs index 37cbda7..653d24f 100644 --- a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=UnionWithConflictingResultTypeName.00.verified.cs +++ b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=UnionWithConflictingResultTypeName.00.verified.cs @@ -14,6 +14,9 @@ internal sealed class DiscriminatedUnionAttribute : global::System.Attribute /// Generates exhaustive Match and Switch methods for the entire type hierarchy. public bool Flatten { get; set; } + /// If a specialized partition extension method for IEnumerable should be generated. Defaults to . + public bool GeneratePartitionExtension { get; set; } + /// Customized the generic type name used for the result in the generated Match methods. Defaults to TResult. public string? MatchResultTypeName { get; set; } } diff --git a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=UnionWithConflictingResultTypeName.01.verified.cs b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=UnionWithConflictingResultTypeName.01.verified.cs index e44849b..6ba348d 100644 --- a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=UnionWithConflictingResultTypeName.01.verified.cs +++ b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=UnionWithConflictingResultTypeName.01.verified.cs @@ -4,18 +4,18 @@ partial record UnionWithConflictingGenericType { - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public abstract TMatchResult Match(global::System.Func variant); - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public abstract void Switch(global::System.Action variant); partial record Variant { - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override TMatchResult Match(global::System.Func variant) => variant(this); - [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.1.0.0")] + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] public override void Switch(global::System.Action variant) => variant(this); } } diff --git a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=UnionWithPartitionUsage.00.verified.cs b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=UnionWithPartitionUsage.00.verified.cs new file mode 100644 index 0000000..653d24f --- /dev/null +++ b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=UnionWithPartitionUsage.00.verified.cs @@ -0,0 +1,23 @@ +//HintName: DiscriminatedUnionAttribute.g.cs +// +#nullable enable + +namespace Funcky +{ + [global::System.Diagnostics.Conditional("Funcky_DiscriminatedUnion")] + [global::System.AttributeUsage(global::System.AttributeTargets.Class)] + internal sealed class DiscriminatedUnionAttribute : global::System.Attribute + { + /// Allow only consumers in the same assembly to use the exhaustive Match and Switch methods. + public bool NonExhaustive { get; set; } + + /// Generates exhaustive Match and Switch methods for the entire type hierarchy. + public bool Flatten { get; set; } + + /// If a specialized partition extension method for IEnumerable should be generated. Defaults to . + public bool GeneratePartitionExtension { get; set; } + + /// Customized the generic type name used for the result in the generated Match methods. Defaults to TResult. + public string? MatchResultTypeName { get; set; } + } +} diff --git a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=UnionWithPartitionUsage.01.verified.cs b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=UnionWithPartitionUsage.01.verified.cs new file mode 100644 index 0000000..be2fb07 --- /dev/null +++ b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.GeneratesExpectedSourceCode_sourceFileName=UnionWithPartitionUsage.01.verified.cs @@ -0,0 +1,70 @@ +//HintName: DiscriminatedUnionGenerator.g.cs +// +#nullable enable + +namespace Funcky.DiscriminatedUnion.Test.Sources +{ + partial record UnionWithPartitionUsage + { + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] + public abstract TResult Match(global::System.Func success, global::System.Func warning, global::System.Func error); + + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] + public abstract void Switch(global::System.Action success, global::System.Action warning, global::System.Action error); + + partial record Success + { + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] + public override TResult Match(global::System.Func success, global::System.Func warning, global::System.Func error) => success(this); + + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] + public override void Switch(global::System.Action success, global::System.Action warning, global::System.Action error) => success(this); + } + + partial record Warning + { + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] + public override TResult Match(global::System.Func success, global::System.Func warning, global::System.Func error) => warning(this); + + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] + public override void Switch(global::System.Action success, global::System.Action warning, global::System.Action error) => warning(this); + } + + partial record Error + { + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] + public override TResult Match(global::System.Func success, global::System.Func warning, global::System.Func error) => error(this); + + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] + public override void Switch(global::System.Action success, global::System.Action warning, global::System.Action error) => error(this); + } + } + + [global::System.CodeDom.Compiler.GeneratedCode("Funcky.DiscriminatedUnion.SourceGeneration", "1.2.0.0")] + public static partial class UnionWithPartitionUsageEnumerableExtensions + { + public static (global::System.Collections.Generic.IReadOnlyList Success, global::System.Collections.Generic.IReadOnlyList Warning, global::System.Collections.Generic.IReadOnlyList Error) Partition(this global::System.Collections.Generic.IEnumerable source) + { + var successItems = new global::System.Collections.Generic.List(); + var warningItems = new global::System.Collections.Generic.List(); + var errorItems = new global::System.Collections.Generic.List(); + foreach (var item in source) + { + item.Switch(success: successItems.Add, warning: warningItems.Add, error: errorItems.Add); + } + return (successItems.AsReadOnly(), warningItems.AsReadOnly(), errorItems.AsReadOnly()); + } + + public static TResult Partition(this global::System.Collections.Generic.IEnumerable source, global::System.Func, global::System.Collections.Generic.IReadOnlyList, global::System.Collections.Generic.IReadOnlyList, TResult> resultSelector) + { + var successItems = new global::System.Collections.Generic.List(); + var warningItems = new global::System.Collections.Generic.List(); + var errorItems = new global::System.Collections.Generic.List(); + foreach (var item in source) + { + item.Switch(success: successItems.Add, warning: warningItems.Add, error: errorItems.Add); + } + return resultSelector(successItems.AsReadOnly(), warningItems.AsReadOnly(), errorItems.AsReadOnly()); + } + } +} diff --git a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.cs b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.cs index c1cde5f..c90b294 100644 --- a/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.cs +++ b/Funcky.DiscriminatedUnion.Test/SourceGeneratorTest.cs @@ -20,6 +20,9 @@ public sealed class SourceGeneratorTest [InlineData("DeeplyNestedUnion")] [InlineData("NonExhaustive")] [InlineData("JsonPolymorphic")] + [InlineData("UnionWithPartitionUsage")] + [InlineData("FlattenedUnionWithPartition")] + [InlineData("FlattenedNestedUnionWithPartition")] public async Task GeneratesExpectedSourceCode(string sourceFileName) => await Verify(sourceFileName); [Fact] diff --git a/Funcky.DiscriminatedUnion.Test/Sources/FlattenedNestedUnionWithPartition.cs b/Funcky.DiscriminatedUnion.Test/Sources/FlattenedNestedUnionWithPartition.cs new file mode 100644 index 0000000..08da131 --- /dev/null +++ b/Funcky.DiscriminatedUnion.Test/Sources/FlattenedNestedUnionWithPartition.cs @@ -0,0 +1,23 @@ +namespace Funcky.DiscriminatedUnion.Test; + +[DiscriminatedUnion(Flatten = true, GeneratePartitionExtension = true)] +public abstract partial record FlattenedNestedUnionWithPartition +{ + public sealed partial record Keyword(string Value) : FlattenedNestedUnionWithPartition; + + public abstract partial record Literal : FlattenedNestedUnionWithPartition + { + public abstract partial record Number : Literal + { + public sealed partial record Integer(int Value) : Number; + } + } +} + +public static class FlattenedNestedUnionWithPartitionTest +{ + public static void Test(FlattenedNestedUnionWithPartition[] items) + { + var (keywords, integers) = items.Partition(); + } +} diff --git a/Funcky.DiscriminatedUnion.Test/Sources/FlattenedUnionWithPartition.cs b/Funcky.DiscriminatedUnion.Test/Sources/FlattenedUnionWithPartition.cs new file mode 100644 index 0000000..74210c5 --- /dev/null +++ b/Funcky.DiscriminatedUnion.Test/Sources/FlattenedUnionWithPartition.cs @@ -0,0 +1,21 @@ +namespace Funcky.DiscriminatedUnion.Test; + +[DiscriminatedUnion(Flatten = true, GeneratePartitionExtension = true)] +public abstract partial record FlattenedUnionWithPartition +{ + public sealed partial record Keyword(string Value) : FlattenedUnionWithPartition; + + public abstract partial record Literal : FlattenedUnionWithPartition; + + public abstract partial record Number : Literal; + + public sealed partial record Integer(int Value) : Number; +} + +public static class FlattenedUnionWithPartitionTest +{ + public static void Test(FlattenedUnionWithPartition[] items) + { + var (keywords, integers) = items.Partition(); + } +} diff --git a/Funcky.DiscriminatedUnion.Test/Sources/UnionWithPartitionUsage.cs b/Funcky.DiscriminatedUnion.Test/Sources/UnionWithPartitionUsage.cs new file mode 100644 index 0000000..2cd0279 --- /dev/null +++ b/Funcky.DiscriminatedUnion.Test/Sources/UnionWithPartitionUsage.cs @@ -0,0 +1,21 @@ +namespace Funcky.DiscriminatedUnion.Test.Sources; + +[DiscriminatedUnion(GeneratePartitionExtension = true)] +public abstract partial record UnionWithPartitionUsage +{ + public sealed partial record Success : UnionWithPartitionUsage; + + public sealed partial record Warning : UnionWithPartitionUsage; + + public sealed partial record Error : UnionWithPartitionUsage; +} + +public static class UnionWithPartitionUsageTest +{ + public static void Test(UnionWithPartitionUsage[] items) + { + var (successes, warnings, errors) = items.Partition(); + + int _ = items.Partition(resultSelector: (_, w, e) => w.Count + e.Count); + } +} diff --git a/Funcky.DiscriminatedUnion.sln b/Funcky.DiscriminatedUnion.sln index 2e812ec..d802bd0 100644 --- a/Funcky.DiscriminatedUnion.sln +++ b/Funcky.DiscriminatedUnion.sln @@ -7,6 +7,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Funcky.DiscriminatedUnion.S EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Funcky.DiscriminatedUnion.Test", "Funcky.DiscriminatedUnion.Test\Funcky.DiscriminatedUnion.Test.csproj", "{B4C3E53F-75BB-4FD4-AF8B-9F715C423766}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_SolutionItems", "_SolutionItems", "{53A014A1-CA3C-476A-ACC5-83CDF144C7AE}" + ProjectSection(SolutionItems) = preProject + changelog.md = changelog.md + global.json = global.json + readme.md = readme.md + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/changelog.md b/changelog.md index 28f6358..53639a4 100644 --- a/changelog.md +++ b/changelog.md @@ -1,4 +1,7 @@ # Changelog +## 1.2.0 +* Add the `GeneratePartitionExtension` option to enable generation of a specialized `Partition` method. + ## 1.1.0 * Add support for generating `[JsonDerivedType]` attributes. diff --git a/global.json b/global.json index 77c776f..959d01c 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "7.0.100", + "version": "8.0.103", "rollForward": "latestFeature" } } diff --git a/readme.md b/readme.md index 4c73082..7fa8ef2 100644 --- a/readme.md +++ b/readme.md @@ -130,4 +130,38 @@ partial record Shape +### `GeneratePartitionExtension` +Set `GeneratePartitionExtension` to true to auto-generate partition methods for `IEnumerable` of your type. + +```cs +using Funcky; +using System.Text.Serialization; + +[DiscriminatedUnion(GeneratePartitionExtension = true)] +public abstract partial record Result +{ + public sealed partial record Ok() : Result; + + public sealed partial record Warning(string Message) : Result; + + public sealed partial record Error(string Message) : Result; +} +``` + +
+ +Usage + +```cs +var results = new Result[] { new Result.Ok(), /* ... */ } + +// N-Tuple extension method: +var (oks, warnings, errors) = results.Partition(); + +// Extension method with result selector: +var warningAndErrorCount = results.Partition(resultSelector: (_, warnings, errors) => warnings.Count + errors.Count); +``` + +
+ [json-polymorphism-docs]: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/polymorphism