Skip to content

Commit 82053c5

Browse files
committed
Added json/messagepack support to ad hoc unions if a factory is present.
1 parent 81ba10f commit 82053c5

File tree

33 files changed

+744
-149
lines changed

33 files changed

+744
-149
lines changed

Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<Copyright>(c) $([System.DateTime]::Now.Year), Pawel Gerr. All rights reserved.</Copyright>
5-
<VersionPrefix>9.1.0</VersionPrefix>
5+
<VersionPrefix>9.2.0</VersionPrefix>
66
<Authors>Pawel Gerr</Authors>
77
<GenerateDocumentationFile>true</GenerateDocumentationFile>
88
<PackageProjectUrl>https://github.com/PawelGerr/Thinktecture.Runtime.Extensions</PackageProjectUrl>

docs

Submodule docs updated from 12cb5e3 to e28184d

samples/Basic.Samples/Unions/DiscriminatedUnionsDemos.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -89,14 +89,12 @@ private static void SerializationDemoForAdHocUnions(ILogger logger)
8989
TextOrNumberSerializable textOrNumberFromString = "text";
9090
TextOrNumberSerializable textOrNumberFromInt = 42;
9191

92-
var jsonOptions = new JsonSerializerOptions { Converters = { new ThinktectureJsonConverterFactory() } };
93-
94-
var json = JsonSerializer.Serialize(textOrNumberFromString, jsonOptions);
95-
var deserializedTextOrNumber = JsonSerializer.Deserialize<TextOrNumberSerializable>(json, jsonOptions);
92+
var json = JsonSerializer.Serialize(textOrNumberFromString);
93+
var deserializedTextOrNumber = JsonSerializer.Deserialize<TextOrNumberSerializable>(json);
9694
logger.Information("TextOrNumberSerializable (de)serialization: {Json} -> {TextOrNumber}", json, deserializedTextOrNumber);
9795

98-
json = JsonSerializer.Serialize(textOrNumberFromInt, jsonOptions);
99-
deserializedTextOrNumber = JsonSerializer.Deserialize<TextOrNumberSerializable>(json, jsonOptions);
96+
json = JsonSerializer.Serialize(textOrNumberFromInt);
97+
deserializedTextOrNumber = JsonSerializer.Deserialize<TextOrNumberSerializable>(json);
10098
logger.Information("TextOrNumberSerializable (de)serialization: {Json} -> {TextOrNumber}", json, deserializedTextOrNumber);
10199
}
102100

samples/Basic.Samples/Unions/TextOrNumberSerializable.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ namespace Thinktecture.Unions;
99
SwitchMethods = SwitchMapMethodsGeneration.DefaultWithPartialOverloads,
1010
MapMethods = SwitchMapMethodsGeneration.DefaultWithPartialOverloads)]
1111
[ObjectFactory<string>(UseForSerialization = SerializationFrameworks.All)]
12-
[JsonConverter(typeof(ThinktectureJsonConverterFactory))] // Optional, ThinktectureJsonConverterFactory can be registered with JsonSerializerOptions
1312
public partial class TextOrNumberSerializable
1413
{
1514
// For serialization (implementation of IConvertible<string>)

src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/AdHocUnions/AdHocUnionCodeGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ namespace ").Append(_state.Namespace).Append(@"
4949

5050
private void GenerateUnion(CancellationToken cancellationToken)
5151
{
52-
_sb.GenerateStructLayoutAttributeIfRequired(_state.IsReferenceType, _state.Settings.HasStructLayoutAttribute);
52+
_sb.GenerateStructLayoutAttributeIfRequired(_state.IsReferenceType, _state.AttributeInfo.HasStructLayoutAttribute);
5353

5454
_sb.Append(@"
5555
[global::System.Diagnostics.CodeAnalysis.SuppressMessage(""ThinktectureRuntimeExtensionsAnalyzer"", ""TTRESG1000:Internal Thinktecture.Runtime.Extensions API usage"")]

src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/AdHocUnions/AdHocUnionSettings.cs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ namespace Thinktecture.CodeAnalysis.AdHocUnions;
22

33
public sealed class AdHocUnionSettings : IEquatable<AdHocUnionSettings>
44
{
5-
private readonly AttributeInfo _attributeInfo;
6-
75
public bool SkipToString { get; }
86
public SwitchMapMethodsGeneration SwitchMethods { get; }
97
public SwitchMapMethodsGeneration MapMethods { get; }
@@ -13,12 +11,11 @@ public sealed class AdHocUnionSettings : IEquatable<AdHocUnionSettings>
1311
public ConversionOperatorsGeneration ConversionFromValue { get; }
1412
public ConversionOperatorsGeneration ConversionToValue { get; }
1513
public string SwitchMapStateParameterName { get; }
16-
public bool HasStructLayoutAttribute => _attributeInfo.HasStructLayoutAttribute;
14+
public SerializationFrameworks SerializationFrameworks { get; }
1715

1816
public AdHocUnionSettings(
1917
AttributeData attribute,
20-
int numberOfMemberTypes,
21-
AttributeInfo attributeInfo)
18+
int numberOfMemberTypes)
2219
{
2320
SkipToString = attribute.FindSkipToString() ?? false;
2421
SwitchMethods = attribute.FindSwitchMethods();
@@ -28,7 +25,7 @@ public AdHocUnionSettings(
2825
ConversionFromValue = attribute.FindConversionFromValue() ?? ConversionOperatorsGeneration.Implicit;
2926
ConversionToValue = attribute.FindConversionToValue() ?? ConversionOperatorsGeneration.Explicit;
3027
SwitchMapStateParameterName = attribute.FindSwitchMapStateParameterName();
31-
_attributeInfo = attributeInfo;
28+
SerializationFrameworks = attribute.FindSerializationFrameworks();
3229

3330
var memberTypeSettings = new AdHocUnionMemberTypeSetting[numberOfMemberTypes];
3431
MemberTypeSettings = memberTypeSettings;
@@ -60,7 +57,7 @@ public bool Equals(AdHocUnionSettings? other)
6057
&& ConversionFromValue == other.ConversionFromValue
6158
&& ConversionToValue == other.ConversionToValue
6259
&& SwitchMapStateParameterName == other.SwitchMapStateParameterName
63-
&& HasStructLayoutAttribute == other.HasStructLayoutAttribute
60+
&& SerializationFrameworks == other.SerializationFrameworks
6461
&& MemberTypeSettings.SequenceEqual(other.MemberTypeSettings);
6562
}
6663

@@ -76,7 +73,7 @@ public override int GetHashCode()
7673
hashCode = (hashCode * 397) ^ (int)ConversionFromValue;
7774
hashCode = (hashCode * 397) ^ (int)ConversionToValue;
7875
hashCode = (hashCode * 397) ^ SwitchMapStateParameterName.GetHashCode();
79-
hashCode = (hashCode * 397) ^ HasStructLayoutAttribute.GetHashCode();
76+
hashCode = (hashCode * 397) ^ (int)SerializationFrameworks;
8077
hashCode = (hashCode * 397) ^ MemberTypeSettings.ComputeHashCode();
8178

8279
return hashCode;

src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/AdHocUnions/AdHocUnionSourceGenState.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,17 @@ public sealed class AdHocUnionSourceGenState
2222

2323
public IReadOnlyList<AdHocUnionMemberTypeState> MemberTypes { get; }
2424
public AdHocUnionSettings Settings { get; }
25+
public AttributeInfo AttributeInfo { get; }
2526

2627
public AdHocUnionSourceGenState(
2728
INamedTypeSymbol type,
2829
IReadOnlyList<AdHocUnionMemberTypeState> memberTypes,
29-
AdHocUnionSettings settings)
30+
AdHocUnionSettings settings,
31+
AttributeInfo attributeInfo)
3032
{
3133
MemberTypes = memberTypes;
3234
Settings = settings;
35+
AttributeInfo = attributeInfo;
3336
Name = type.Name;
3437
Namespace = type.ContainingNamespace?.IsGlobalNamespace == true ? null : type.ContainingNamespace?.ToString();
3538
ContainingTypes = type.GetContainingTypes();
@@ -57,6 +60,7 @@ public bool Equals(AdHocUnionSourceGenState? other)
5760
&& IsReferenceType == other.IsReferenceType
5861
&& IsRefStruct == other.IsRefStruct
5962
&& Settings.Equals(other.Settings)
63+
&& AttributeInfo.Equals(other.AttributeInfo)
6064
&& MemberTypes.SequenceEqual(other.MemberTypes)
6165
&& ContainingTypes.SequenceEqual(other.ContainingTypes);
6266
}
@@ -69,6 +73,7 @@ public override int GetHashCode()
6973
hashCode = (hashCode * 397) ^ IsReferenceType.GetHashCode();
7074
hashCode = (hashCode * 397) ^ IsRefStruct.GetHashCode();
7175
hashCode = (hashCode * 397) ^ Settings.GetHashCode();
76+
hashCode = (hashCode * 397) ^ AttributeInfo.GetHashCode();
7277
hashCode = (hashCode * 397) ^ MemberTypes.ComputeHashCode();
7378
hashCode = (hashCode * 397) ^ ContainingTypes.ComputeHashCode();
7479

src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/AdHocUnions/AdHocUnionSourceGenerator.cs

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,45 @@ private void InitializeUnionSourceGen(
4747
: ImmutableArray<AdHocUnionSourceGenState>.Empty);
4848

4949
InitializeUnionTypeGeneration(context, validStates, options);
50+
InitializeSerializerGenerators(context, validStates, options);
5051

5152
InitializeErrorReporting(context, unionTypeOrError);
5253
InitializeExceptionReporting(context, unionTypeOrError);
5354
}
5455

56+
private ImmutableArray<IKeyedSerializerCodeGeneratorFactory> GetSerializerCodeGeneratorFactories(MetadataReference reference)
57+
{
58+
var factories = ImmutableArray<IKeyedSerializerCodeGeneratorFactory>.Empty;
59+
60+
try
61+
{
62+
foreach (var module in reference.GetModules())
63+
{
64+
switch (module.Name)
65+
{
66+
case Constants.Modules.THINKTECTURE_RUNTIME_EXTENSIONS_JSON:
67+
Logger.LogInformation("Code generator for System.Text.Json will participate in code generation");
68+
factories = factories.Add(JsonAdHocUnionCodeGeneratorFactory.Instance);
69+
break;
70+
case Constants.Modules.THINKTECTURE_RUNTIME_EXTENSIONS_NEWTONSOFT_JSON:
71+
Logger.LogInformation("Code generator for Newtonsoft.Json will participate in code generation");
72+
factories = factories.Add(NewtonsoftJsonAdHocUnionCodeGeneratorFactory.Instance);
73+
break;
74+
case Constants.Modules.THINKTECTURE_RUNTIME_EXTENSIONS_MESSAGEPACK:
75+
Logger.LogInformation("Code generator for MessagePack will participate in code generation");
76+
factories = factories.Add(MessagePackAdHocUnionCodeGeneratorFactory.Instance);
77+
break;
78+
}
79+
}
80+
}
81+
catch (Exception ex)
82+
{
83+
Logger.LogError("Error during checking referenced modules", exception: ex);
84+
}
85+
86+
return factories;
87+
}
88+
5589
private bool IsCandidate(SyntaxNode syntaxNode, CancellationToken cancellationToken)
5690
{
5791
try
@@ -146,8 +180,7 @@ private bool IsUnionCandidate(TypeDeclarationSyntax typeDeclaration)
146180
return new SourceGenContext(new SourceGenError("Could not fetch type information for code generation of a discriminated union", tds));
147181

148182
var settings = new AdHocUnionSettings(context.Attributes[0],
149-
attributeType.Arity,
150-
attributeInfo);
183+
attributeType.Arity);
151184
var memberTypeStates = attributeType.Arity == 0 ? [] : new AdHocUnionMemberTypeState[attributeType.Arity];
152185

153186
for (var i = 0; i < attributeType.TypeArguments.Length; i++)
@@ -203,7 +236,8 @@ private bool IsUnionCandidate(TypeDeclarationSyntax typeDeclaration)
203236

204237
var unionState = new AdHocUnionSourceGenState(type,
205238
memberTypeStates,
206-
settings);
239+
settings,
240+
attributeInfo);
207241

208242
Logger.LogDebug("The type declaration is a valid union", null, unionState);
209243

@@ -237,6 +271,38 @@ private void InitializeUnionTypeGeneration(
237271
context.RegisterSourceOutput(unionTypes.Combine(options), (ctx, tuple) => GenerateCode(ctx, tuple.Left, tuple.Right, AdHocUnionCodeGeneratorFactory.Instance));
238272
}
239273

274+
private void InitializeSerializerGenerators(IncrementalGeneratorInitializationContext context, IncrementalValuesProvider<AdHocUnionSourceGenState> validStates, IncrementalValueProvider<GeneratorOptions> options)
275+
{
276+
var serializerGeneratorFactories = context.MetadataReferencesProvider
277+
.SelectMany((reference, _) => GetSerializerCodeGeneratorFactories(reference))
278+
.Collect()
279+
.Select(static (states, _) => states.IsDefaultOrEmpty
280+
? ImmutableArray<IKeyedSerializerCodeGeneratorFactory>.Empty
281+
: states.Distinct())
282+
.WithComparer(new SetComparer<IKeyedSerializerCodeGeneratorFactory>());
283+
284+
var serializerGeneratorStates = validStates.Select((state, _) => new KeyedSerializerGeneratorState(
285+
state,
286+
null,
287+
state.AttributeInfo,
288+
state.Settings.SerializationFrameworks))
289+
.Combine(serializerGeneratorFactories)
290+
.SelectMany((tuple, _) => ImmutableArray.CreateRange(tuple.Right, (factory, state) => (State: state, Factory: factory), tuple.Left))
291+
.Where(tuple =>
292+
{
293+
if (tuple.Factory.MustGenerateCode(tuple.State))
294+
{
295+
Logger.LogDebug("Code generator must generate code.", null, tuple.State, factory: tuple.Factory);
296+
return true;
297+
}
298+
299+
Logger.LogInformation("Code generator must not generate code.", null, tuple.State, factory: tuple.Factory);
300+
return false;
301+
});
302+
303+
context.RegisterImplementationSourceOutput(serializerGeneratorStates.Combine(options), (ctx, tuple) => GenerateCode(ctx, tuple.Left.State, tuple.Right, tuple.Left.Factory));
304+
}
305+
240306
private void InitializeErrorReporting(
241307
IncrementalGeneratorInitializationContext context,
242308
IncrementalValuesProvider<SourceGenContext> unionTypeOrException)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace Thinktecture.CodeAnalysis.AdHocUnions;
2+
3+
public sealed class JsonAdHocUnionCodeGeneratorFactory : JsonKeyedSerializerCodeGeneratorFactoryBase
4+
{
5+
public static readonly IKeyedSerializerCodeGeneratorFactory Instance = new JsonAdHocUnionCodeGeneratorFactory();
6+
7+
public override string CodeGeneratorName => "SystemTextJson-AdHocUnion-CodeGenerator";
8+
9+
private JsonAdHocUnionCodeGeneratorFactory()
10+
{
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace Thinktecture.CodeAnalysis.AdHocUnions;
2+
3+
public sealed class MessagePackAdHocUnionCodeGeneratorFactory : MessagePackKeyedSerializerCodeGeneratorFactoryBase
4+
{
5+
public static readonly IKeyedSerializerCodeGeneratorFactory Instance = new MessagePackAdHocUnionCodeGeneratorFactory();
6+
7+
public override string CodeGeneratorName => "MessagePack-AdHocUnion-CodeGenerator";
8+
9+
private MessagePackAdHocUnionCodeGeneratorFactory()
10+
{
11+
}
12+
}

0 commit comments

Comments
 (0)