From f0371191c87ecf18ab0f0e407e3679ec007ec52e Mon Sep 17 00:00:00 2001 From: inputfalken Date: Sat, 16 Sep 2023 15:17:00 +0200 Subject: [PATCH] Format code & add exception name to constants --- .../Constants.cs | 10 +- .../DynamoDBDocument/Generation.cs | 254 +++++++++--------- .../NotNullEvaluationExtensions.cs | 2 +- 3 files changed, 130 insertions(+), 136 deletions(-) diff --git a/DynamoDBGenerator.SourceGenerator/Constants.cs b/DynamoDBGenerator.SourceGenerator/Constants.cs index e84d0477..a006b7e8 100644 --- a/DynamoDBGenerator.SourceGenerator/Constants.cs +++ b/DynamoDBGenerator.SourceGenerator/Constants.cs @@ -1,4 +1,6 @@ +using System; using DynamoDBGenerator.Attributes; +using DynamoDBGenerator.Exceptions; using Microsoft.CodeAnalysis; namespace DynamoDBGenerator.SourceGenerator; @@ -10,13 +12,7 @@ public static class Constants public const string MarshallerAttributeName = nameof(DynamoDBMarshallerAttribute); public const string MarshallerConstructorAttributeName = nameof(DynamoDBMarshallerConstructorAttribute); public const string DynamoDbDocumentPropertyFullname = $"{AssemblyName}.{AttributeNameSpace}.{MarshallerAttributeName}"; - - public static class Errors - { - - - } - + public const string MarshallingExceptionName = nameof(DynamoDBMarshallingException); public const string NewLine = @" "; diff --git a/DynamoDBGenerator.SourceGenerator/Extensions/CodeGeneration/DynamoDBDocument/Generation.cs b/DynamoDBGenerator.SourceGenerator/Extensions/CodeGeneration/DynamoDBDocument/Generation.cs index 3fef3b44..25196ec5 100644 --- a/DynamoDBGenerator.SourceGenerator/Extensions/CodeGeneration/DynamoDBDocument/Generation.cs +++ b/DynamoDBGenerator.SourceGenerator/Extensions/CodeGeneration/DynamoDBDocument/Generation.cs @@ -1,6 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; using Amazon.DynamoDBv2.Model; using DynamoDBGenerator.SourceGenerator.Types; using Microsoft.CodeAnalysis; @@ -12,21 +9,21 @@ public class DynamoDbMarshaller private const string DeserializeName = "Unmarshall"; private const string InterfaceName = "IDynamoDBMarshaller"; private const string KeysName = "Keys"; + private const string NameTrackerName = "AttributeNameExpressionTracker"; private const string PartitionKeyName = "PartitionKey"; private const string RangeKeyName = "RangeKey"; - private const string ValueTrackerName = "AttributeExpressionValueTracker"; - private const string NameTrackerName = "AttributeNameExpressionTracker"; private const string SerializeName = "Marshall"; + private const string ValueTrackerName = "AttributeExpressionValueTracker"; + private readonly INamedTypeSymbol _argumentTypeSymbol; + private readonly Func _attributeNameAssignmentNameFactory; + private readonly Func _attributeValueAssignmentNameFactory; private readonly IEqualityComparer _comparer; private readonly Func _deserializationMethodNameFactory; + private readonly INamedTypeSymbol _entityTypeSymbol; private readonly Func _fullTypeNameFactory; - private readonly Func _serializationMethodNameFactory; private readonly Func _keysMethodNameFactory; - private readonly Func _attributeNameAssignmentNameFactory; - private readonly Func _attributeValueAssignmentNameFactory; - private readonly INamedTypeSymbol _entityTypeSymbol; - private readonly INamedTypeSymbol _argumentTypeSymbol; private readonly string _publicAccessPropertyName; + private readonly Func _serializationMethodNameFactory; public DynamoDbMarshaller(in DynamoDBMarshallerArguments arguments, IEqualityComparer comparer) { @@ -117,6 +114,7 @@ private Assignment BuildPocoList(in SingleGeneric singleGeneric, in string? oper return null; } + private static Assignment? BuildSet(in ITypeSymbol elementType, in string accessPattern) { var newAccessPattern = elementType.NotNullLambdaExpression() is { } expression @@ -130,6 +128,7 @@ private Assignment BuildPocoList(in SingleGeneric singleGeneric, in string? oper ? null : elementType.ToInlineAssignment($"NS = new List({newAccessPattern}.Select(x => x.ToString()))"); } + private string CreateAttributePocoFactory() { var enumerable = Conversion.ConversionMethods( @@ -222,8 +221,8 @@ public string CreateDynamoDbDocumentProperty(Accessibility accessibility) private Assignment DataMemberAssignment(in ITypeSymbol type, in string pattern, in string memberName) { - - var defaultCase = type.IsNullable() ? "_ => null" : @$"_ => throw new DynamoDBMarshallingException(""{memberName}"", ""{Constants.NotNullErrorMessage}"")"; + + var defaultCase = type.IsNullable() ? "_ => null" : @$"_ => throw new {Constants.MarshallingExceptionName}(""{memberName}"", ""{Constants.NotNullErrorMessage}"")"; return Execution(in type, in pattern, defaultCase, in memberName); Assignment Execution(in ITypeSymbol typeSymbol, in string accessPattern, string @default, in string memberName) @@ -275,8 +274,7 @@ Assignment ExternalAssignment(in ITypeSymbol typeSymbol, in string accessPattern } } - - private Conversion ExpressionAttributeValue(ITypeSymbol typeSymbol) + private Conversion ExpressionAttributeName(ITypeSymbol typeSymbol) { var dataMembers = typeSymbol .GetDynamoDbProperties() @@ -284,55 +282,52 @@ private Conversion ExpressionAttributeValue(ITypeSymbol typeSymbol) .Select(x => ( IsKnown: x.DataMember.Type.GetKnownType() is not null, DDB: x, - ValueRef: $"_{x.DataMember.Name}ValueRef", - AttributeReference: _attributeValueAssignmentNameFactory(x.DataMember.Type), - AttributeInterfaceName: AttributeInterfaceName(x.DataMember.Type))) + NameRef: $"_{x.DataMember.Name}NameRef", + AttributeReference: _attributeNameAssignmentNameFactory(x.DataMember.Type), + AttributeInterfaceName: AttributeInterfaceName())) .ToArray(); var fieldDeclarations = dataMembers .Select(static x => x.IsKnown - ? $@" private readonly Lazy {x.ValueRef}; - public string {x.DDB.DataMember.Name} => {x.ValueRef}.Value;" + ? $@" private readonly Lazy {x.NameRef}; + public string {x.DDB.DataMember.Name} => {x.NameRef}.Value;" : $@" private readonly Lazy<{x.AttributeReference}> _{x.DDB.DataMember.Name}; public {x.AttributeReference} {x.DDB.DataMember.Name} => _{x.DDB.DataMember.Name}.Value;" ); - const string valueProvider = "valueIdProvider"; + const string constructorAttributeName = "nameRef"; var fieldAssignments = dataMembers .Select(static x => { + var ternaryExpressionName = + $"{constructorAttributeName} is null ? {@$"""#{x.DDB.AttributeName}"""}: {@$"$""{{{constructorAttributeName}}}.#{x.DDB.AttributeName}"""}"; var assignment = x.IsKnown - ? $@" {x.ValueRef} = new ({valueProvider});" - : $@" _{x.DDB.DataMember.Name} = new (() => new {x.AttributeReference}({valueProvider}));"; + ? $@" {x.NameRef} = new (() => {ternaryExpressionName});" + : $@" _{x.DDB.DataMember.Name} = new (() => new {x.AttributeReference}({ternaryExpressionName}));"; return new Assignment(assignment, x.DDB.DataMember.Type, x.IsKnown is false); }) .ToArray(); - var className = _attributeValueAssignmentNameFactory(typeSymbol); - var constructor = $@"public {className}(Func {valueProvider}) + var className = _attributeNameAssignmentNameFactory(typeSymbol); + var constructor = $@"public {className}(string? {constructorAttributeName}) {{ {string.Join(Constants.NewLine, fieldAssignments.Select(x => x.Value))} }}"; - var expressionAttributeValueYields = dataMembers - .Select(x => - { - var accessPattern = $"entity.{x.DDB.DataMember.Name}"; - return x.IsKnown - ? $@" if ({x.ValueRef}.IsValueCreated) {x.DDB.DataMember.Type.NotNullIfStatement(accessPattern, $"yield return new ({x.ValueRef}.Value, {AttributeValueAssignment(x.DDB.DataMember.Type, $"entity.{x.DDB.DataMember.Name}").ToAttributeValue()});")}" - : $@" if (_{x.DDB.DataMember.Name}.IsValueCreated) {x.DDB.DataMember.Type.NotNullIfStatement(accessPattern, $"foreach (var x in ({x.DDB.DataMember.Name} as {x.AttributeInterfaceName}).{nameof(IExpressionAttributeValueTracker.AccessedValues)}({accessPattern})) {{ yield return x; }}")}"; - }); + var expressionAttributeNameYields = dataMembers.Select(static x => x.IsKnown + ? $@" if ({x.NameRef}.IsValueCreated) yield return new ({x.NameRef}.Value, ""{x.DDB.AttributeName}"");" + : $" if (_{x.DDB.DataMember.Name}.IsValueCreated) foreach (var x in ({x.DDB.DataMember.Name} as {x.AttributeInterfaceName}).{nameof(IExpressionAttributeNameTracker.AccessedNames)}()) {{ yield return x; }}"); - var interfaceName = AttributeInterfaceName(typeSymbol); + var interfaceName = AttributeInterfaceName(); var @class = CodeGenerationExtensions.CreateStruct( Accessibility.Public, $"{className} : {interfaceName}", $@"{constructor} {string.Join(Constants.NewLine, fieldDeclarations)} - IEnumerable> {interfaceName}.{nameof(IExpressionAttributeValueTracker.AccessedValues)}({_fullTypeNameFactory(typeSymbol)} entity) + IEnumerable> {interfaceName}.{nameof(IExpressionAttributeNameTracker.AccessedNames)}() {{ -{(string.Join(Constants.NewLine, expressionAttributeValueYields) is var joinedValues && joinedValues != string.Empty ? joinedValues : "return Enumerable.Empty>();")} +{(string.Join(Constants.NewLine, expressionAttributeNameYields) is var joinedNames && joinedNames != string.Empty ? joinedNames : "return Enumerable.Empty>();")} }}", 0, isReadonly: true, @@ -340,63 +335,66 @@ private Conversion ExpressionAttributeValue(ITypeSymbol typeSymbol) ); return new Conversion(@class, fieldAssignments.Where(x => x.HasExternalDependency)); - string AttributeInterfaceName(ITypeSymbol symbol) => $"{nameof(IExpressionAttributeValueTracker)}<{_fullTypeNameFactory(symbol)}>"; + string AttributeInterfaceName() => nameof(IExpressionAttributeNameTracker); } - private Conversion ExpressionAttributeName(ITypeSymbol typeSymbol) - { + private Conversion ExpressionAttributeValue(ITypeSymbol typeSymbol) + { var dataMembers = typeSymbol .GetDynamoDbProperties() .Where(static x => x.IsIgnored is false) .Select(x => ( IsKnown: x.DataMember.Type.GetKnownType() is not null, DDB: x, - NameRef: $"_{x.DataMember.Name}NameRef", - AttributeReference: _attributeNameAssignmentNameFactory(x.DataMember.Type), - AttributeInterfaceName: AttributeInterfaceName())) + ValueRef: $"_{x.DataMember.Name}ValueRef", + AttributeReference: _attributeValueAssignmentNameFactory(x.DataMember.Type), + AttributeInterfaceName: AttributeInterfaceName(x.DataMember.Type))) .ToArray(); var fieldDeclarations = dataMembers .Select(static x => x.IsKnown - ? $@" private readonly Lazy {x.NameRef}; - public string {x.DDB.DataMember.Name} => {x.NameRef}.Value;" + ? $@" private readonly Lazy {x.ValueRef}; + public string {x.DDB.DataMember.Name} => {x.ValueRef}.Value;" : $@" private readonly Lazy<{x.AttributeReference}> _{x.DDB.DataMember.Name}; public {x.AttributeReference} {x.DDB.DataMember.Name} => _{x.DDB.DataMember.Name}.Value;" ); - const string constructorAttributeName = "nameRef"; + const string valueProvider = "valueIdProvider"; var fieldAssignments = dataMembers .Select(static x => { - var ternaryExpressionName = - $"{constructorAttributeName} is null ? {@$"""#{x.DDB.AttributeName}"""}: {@$"$""{{{constructorAttributeName}}}.#{x.DDB.AttributeName}"""}"; var assignment = x.IsKnown - ? $@" {x.NameRef} = new (() => {ternaryExpressionName});" - : $@" _{x.DDB.DataMember.Name} = new (() => new {x.AttributeReference}({ternaryExpressionName}));"; + ? $@" {x.ValueRef} = new ({valueProvider});" + : $@" _{x.DDB.DataMember.Name} = new (() => new {x.AttributeReference}({valueProvider}));"; return new Assignment(assignment, x.DDB.DataMember.Type, x.IsKnown is false); }) .ToArray(); - var className = _attributeNameAssignmentNameFactory(typeSymbol); - var constructor = $@"public {className}(string? {constructorAttributeName}) + var className = _attributeValueAssignmentNameFactory(typeSymbol); + var constructor = $@"public {className}(Func {valueProvider}) {{ {string.Join(Constants.NewLine, fieldAssignments.Select(x => x.Value))} }}"; - var expressionAttributeNameYields = dataMembers.Select(static x => x.IsKnown - ? $@" if ({x.NameRef}.IsValueCreated) yield return new ({x.NameRef}.Value, ""{x.DDB.AttributeName}"");" - : $@" if (_{x.DDB.DataMember.Name}.IsValueCreated) foreach (var x in ({x.DDB.DataMember.Name} as {x.AttributeInterfaceName}).{nameof(IExpressionAttributeNameTracker.AccessedNames)}()) {{ yield return x; }}"); + var expressionAttributeValueYields = dataMembers + .Select(x => + { + var accessPattern = $"entity.{x.DDB.DataMember.Name}"; + return x.IsKnown + ? $" if ({x.ValueRef}.IsValueCreated) {x.DDB.DataMember.Type.NotNullIfStatement(accessPattern, $"yield return new ({x.ValueRef}.Value, {AttributeValueAssignment(x.DDB.DataMember.Type, $"entity.{x.DDB.DataMember.Name}").ToAttributeValue()});")}" + : $" if (_{x.DDB.DataMember.Name}.IsValueCreated) {x.DDB.DataMember.Type.NotNullIfStatement(accessPattern, $"foreach (var x in ({x.DDB.DataMember.Name} as {x.AttributeInterfaceName}).{nameof(IExpressionAttributeValueTracker.AccessedValues)}({accessPattern})) {{ yield return x; }}")}"; + }); - var interfaceName = AttributeInterfaceName(); + var interfaceName = AttributeInterfaceName(typeSymbol); var @class = CodeGenerationExtensions.CreateStruct( Accessibility.Public, $"{className} : {interfaceName}", $@"{constructor} {string.Join(Constants.NewLine, fieldDeclarations)} - IEnumerable> {interfaceName}.{nameof(IExpressionAttributeNameTracker.AccessedNames)}() + IEnumerable> {interfaceName}.{nameof(IExpressionAttributeValueTracker.AccessedValues)}({_fullTypeNameFactory(typeSymbol)} entity) {{ -{(string.Join(Constants.NewLine, expressionAttributeNameYields) is var joinedNames && joinedNames != string.Empty ? joinedNames : "return Enumerable.Empty>();")} +{(string.Join(Constants.NewLine, expressionAttributeValueYields) is var joinedValues && joinedValues != string.Empty ? joinedValues : "return Enumerable.Empty>();")} }}", 0, isReadonly: true, @@ -404,7 +402,78 @@ private Conversion ExpressionAttributeName(ITypeSymbol typeSymbol) ); return new Conversion(@class, fieldAssignments.Where(x => x.HasExternalDependency)); - string AttributeInterfaceName() => nameof(IExpressionAttributeNameTracker); + string AttributeInterfaceName(ITypeSymbol symbol) => $"{nameof(IExpressionAttributeValueTracker)}<{_fullTypeNameFactory(symbol)}>"; + } + private Conversion StaticAttributeValueDictionaryFactory(ITypeSymbol type, KeyStrategy keyStrategy) + { + const string paramReference = "entity"; + const string dictionaryName = "attributeValues"; + var properties = GetAssignments(type, keyStrategy).ToArray(); + + const string indent = " "; + var method = + @$" public static Dictionary {_serializationMethodNameFactory(type)}({_fullTypeNameFactory(type)} {paramReference}) + {{ + {InitializeDictionary(dictionaryName, properties.Select(static x => x.capacityTernary))} + {string.Join(Constants.NewLine + indent, properties.Select(static x => x.dictionaryPopulation))} + return {dictionaryName}; + }}"; + + return new Conversion( + in method, + properties + .Select(static x => x.assignment) + .Where(static x => x.HasExternalDependency) + ); + + IEnumerable<(string dictionaryPopulation, string capacityTernary, Assignment assignment)> GetAssignments( + INamespaceOrTypeSymbol typeSymbol, + KeyStrategy strategy + ) + { + var dynamoDbProperties = strategy switch + { + KeyStrategy.Ignore => typeSymbol.GetDynamoDbProperties() + .Where(static x => x is {IsRangeKey: false, IsHashKey: false}), + KeyStrategy.Only => typeSymbol.GetDynamoDbProperties() + .Where(static x => x.IsHashKey || x.IsRangeKey), + KeyStrategy.Include => typeSymbol.GetDynamoDbProperties(), + _ => throw new ArgumentOutOfRangeException() + }; + + foreach (var x in dynamoDbProperties) + { + if (x.IsIgnored) + continue; + + var accessPattern = $"{paramReference}.{x.DataMember.Name}"; + var attributeValue = AttributeValueAssignment(x.DataMember.Type, accessPattern); + if (strategy == KeyStrategy.Only && attributeValue.HasExternalDependency) + continue; + + var dictionaryAssignment = x.DataMember.Type.NotNullIfStatement( + in accessPattern, + @$"{dictionaryName}.Add(""{x.AttributeName}"", {attributeValue.ToAttributeValue()});" + ); + + var capacityTernaries = x.DataMember.Type.NotNullTernaryExpression(in accessPattern, "1", "0"); + + yield return (dictionaryAssignment, capacityTernaries, attributeValue); + } + } + + static string InitializeDictionary(string dictionaryName, IEnumerable capacityCalculations) + { + var capacityCalculation = string.Join(" + ", capacityCalculations); + + return string.Join(" + ", capacityCalculation)switch + { + "" => $"var {dictionaryName} = new Dictionary(capacity: 0);", + var capacities => $@"var capacity = {capacities}; + var {dictionaryName} = new Dictionary(capacity: capacity); + if (capacity is 0) {{ return {dictionaryName}; }} " + }; + } } private string StaticAttributeValueDictionaryKeys( ITypeSymbol type, @@ -479,77 +548,6 @@ static string InitializeDictionary(string dictionaryName, IEnumerable ca }; } } - private Conversion StaticAttributeValueDictionaryFactory(ITypeSymbol type, KeyStrategy keyStrategy) - { - const string paramReference = "entity"; - const string dictionaryName = "attributeValues"; - var properties = GetAssignments(type, keyStrategy).ToArray(); - - const string indent = " "; - var method = - @$" public static Dictionary {_serializationMethodNameFactory(type)}({_fullTypeNameFactory(type)} {paramReference}) - {{ - {InitializeDictionary(dictionaryName, properties.Select(static x => x.capacityTernary))} - {string.Join(Constants.NewLine + indent, properties.Select(static x => x.dictionaryPopulation))} - return {dictionaryName}; - }}"; - - return new Conversion( - in method, - properties - .Select(static x => x.assignment) - .Where(static x => x.HasExternalDependency) - ); - - IEnumerable<(string dictionaryPopulation, string capacityTernary, Assignment assignment)> GetAssignments( - INamespaceOrTypeSymbol typeSymbol, - KeyStrategy strategy - ) - { - var dynamoDbProperties = strategy switch - { - KeyStrategy.Ignore => typeSymbol.GetDynamoDbProperties() - .Where(static x => x is {IsRangeKey: false, IsHashKey: false}), - KeyStrategy.Only => typeSymbol.GetDynamoDbProperties() - .Where(static x => x.IsHashKey || x.IsRangeKey), - KeyStrategy.Include => typeSymbol.GetDynamoDbProperties(), - _ => throw new ArgumentOutOfRangeException() - }; - - foreach (var x in dynamoDbProperties) - { - if (x.IsIgnored) - continue; - - var accessPattern = $"{paramReference}.{x.DataMember.Name}"; - var attributeValue = AttributeValueAssignment(x.DataMember.Type, accessPattern); - if (strategy == KeyStrategy.Only && attributeValue.HasExternalDependency) - continue; - - var dictionaryAssignment = x.DataMember.Type.NotNullIfStatement( - in accessPattern, - @$"{dictionaryName}.Add(""{x.AttributeName}"", {attributeValue.ToAttributeValue()});" - ); - - var capacityTernaries = x.DataMember.Type.NotNullTernaryExpression(in accessPattern, "1", "0"); - - yield return (dictionaryAssignment, capacityTernaries, attributeValue); - } - } - - static string InitializeDictionary(string dictionaryName, IEnumerable capacityCalculations) - { - var capacityCalculation = string.Join(" + ", capacityCalculations); - - return string.Join(" + ", capacityCalculation)switch - { - "" => $"var {dictionaryName} = new Dictionary(capacity: 0);", - var capacities => $@"var capacity = {capacities}; - var {dictionaryName} = new Dictionary(capacity: capacity); - if (capacity is 0) {{ return {dictionaryName}; }} " - }; - } - } private Conversion StaticPocoFactory(ITypeSymbol type) { diff --git a/DynamoDBGenerator.SourceGenerator/Extensions/CodeGeneration/NotNullEvaluationExtensions.cs b/DynamoDBGenerator.SourceGenerator/Extensions/CodeGeneration/NotNullEvaluationExtensions.cs index b1982c27..92bc6cfd 100644 --- a/DynamoDBGenerator.SourceGenerator/Extensions/CodeGeneration/NotNullEvaluationExtensions.cs +++ b/DynamoDBGenerator.SourceGenerator/Extensions/CodeGeneration/NotNullEvaluationExtensions.cs @@ -36,7 +36,7 @@ public static bool IsNullable(this ITypeSymbol typeSymbol) private static string CreateException(in string accessPattern) { - return @$"throw new DynamoDBMarshallingException(nameof({accessPattern}),""{Constants.NotNullErrorMessage}"");"; + return @$"throw new {Constants.MarshallingExceptionName}(nameof({accessPattern}),""{Constants.NotNullErrorMessage}"");"; } public static string NotNullIfStatement(this ITypeSymbol typeSymbol, in string accessPattern, in string truthy)