-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathAttributeExpressionName.cs
113 lines (95 loc) · 6.22 KB
/
AttributeExpressionName.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
using System.Collections.Immutable;
using DynamoDBGenerator.SourceGenerator.Extensions;
using DynamoDBGenerator.SourceGenerator.Types;
using Microsoft.CodeAnalysis;
using static DynamoDBGenerator.SourceGenerator.Constants.DynamoDBGenerator.Marshaller;
namespace DynamoDBGenerator.SourceGenerator.Generations;
public static class AttributeExpressionName
{
private const string ConstructorAttributeName = "nameRef";
private const string ConstructorSetName = "set";
private const string SetFieldName = "___set___";
private static readonly Func<ITypeSymbol, string> TypeName = TypeExtensions.SuffixedTypeSymbolNameFactory("Names", SymbolEqualityComparer.Default);
internal static IEnumerable<string> CreateClasses(IEnumerable<DynamoDBMarshallerArguments> arguments, Func<ITypeSymbol, ImmutableArray<DynamoDbDataMember>> getDynamoDbProperties, MarshallerOptions options)
{
// Using _comparer can double classes when there's a None nullable property mixed with a nullable property
var hashSet = new HashSet<ITypeSymbol>(SymbolEqualityComparer.Default);
return arguments
.SelectMany(x => CodeFactory.Create(x.EntityTypeSymbol, y => CreateStruct(y, getDynamoDbProperties, options), hashSet));
}
private static IEnumerable<string> TypeContent(
ITypeSymbol typeSymbol,
(bool IsUnknown, DynamoDbDataMember DDB, string DbRef, string AttributeReference, string AttributeInterfaceName)[] dataMembers,
string structName)
{
const string self = "_this";
var constructorFieldAssignments = dataMembers
.Select(x =>
{
var ternaryExpressionName = $"{ConstructorAttributeName} is null ? {@$"""#{x.DDB.AttributeName}"""}: {@$"$""{{{ConstructorAttributeName}}}.#{x.DDB.AttributeName}"""}";
return x.IsUnknown
? $"{x.DDB.DataMember.NameAsPrivateField} = new (() => new {x.AttributeReference}({ternaryExpressionName}, {ConstructorSetName}));"
: $"{x.DDB.DataMember.NameAsPrivateField} = new (() => {ternaryExpressionName});";
})
.Append($"{SetFieldName} = {ConstructorSetName};")
.Append($@"{self} = new(() => {ConstructorAttributeName} ?? throw new NotImplementedException(""Root element AttributeExpressionName reference.""));");
foreach (var fieldAssignment in $"public {structName}(string? {ConstructorAttributeName}, HashSet<KeyValuePair<string, string>> {ConstructorSetName})".CreateScope(constructorFieldAssignments))
yield return fieldAssignment;
foreach (var fieldDeclaration in dataMembers)
{
if (fieldDeclaration.IsUnknown)
{
yield return $"private readonly Lazy<{fieldDeclaration.AttributeReference}> {fieldDeclaration.DDB.DataMember.NameAsPrivateField};";
yield return $"public {fieldDeclaration.AttributeReference} {fieldDeclaration.DDB.DataMember.Name} => {fieldDeclaration.DDB.DataMember.NameAsPrivateField}.Value;";
}
else
{
yield return $"private readonly Lazy<string> {fieldDeclaration.DDB.DataMember.NameAsPrivateField};";
yield return $"public string {fieldDeclaration.DDB.DataMember.Name} => {fieldDeclaration.DDB.DataMember.NameAsPrivateField}.Value;";
}
}
yield return $"private readonly Lazy<string> {self};";
yield return $"private readonly HashSet<KeyValuePair<string, string>> {SetFieldName};";
var yields = dataMembers
.SelectMany(YieldSelector)
.Append($@"if ({self}.IsValueCreated) yield return new ({self}.Value, ""{typeSymbol.Name}"");");
foreach (var s in $"IEnumerable<KeyValuePair<string, string>> {AttributeExpressionNameTrackerInterface}.{AttributeExpressionNameTrackerInterfaceAccessedNames}()".CreateScope(yields))
yield return s;
yield return $"public override string ToString() => {self}.Value;";
}
private static IEnumerable<string> YieldSelector((bool IsUnknown, DynamoDbDataMember DDB, string DbRef, string AttributeReference, string AttributeInterfaceName) x)
{
var camelCase = x.DDB.DataMember.NameAsCamelCase;
if (x.IsUnknown)
{
var scope = $@"if (new KeyValuePair<string, string>(""{x.DbRef}"", ""{x.DDB.AttributeName}"") is var {camelCase} && {SetFieldName}.Add({camelCase}))"
.CreateScope($"yield return {camelCase};")
.Concat($"foreach (var x in ({x.DDB.DataMember.Name} as {x.AttributeInterfaceName}).{AttributeExpressionNameTrackerInterfaceAccessedNames}())".CreateScope("yield return x;"));
return $"if ({x.DDB.DataMember.NameAsPrivateField}.IsValueCreated)".CreateScope(scope);
}
else
{
return $@"if ({x.DDB.DataMember.NameAsPrivateField}.IsValueCreated && new KeyValuePair<string, string>(""{x.DbRef}"", ""{x.DDB.AttributeName}"") is var {camelCase} && {SetFieldName}.Add({camelCase}))".CreateScope($"yield return {camelCase};");
}
}
private static CodeFactory CreateStruct(ITypeSymbol typeSymbol, Func<ITypeSymbol, ImmutableArray<DynamoDbDataMember>> fn, MarshallerOptions options)
{
var dataMembers = fn(typeSymbol)
.Select(x => (
IsUnknown: !options.IsConvertable(x.DataMember.Type) && x.DataMember.Type.TypeIdentifier() is UnknownType,
DDB: x,
DbRef: $"#{x.AttributeName}",
AttributeReference: TypeName(x.DataMember.Type),
AttributeInterfaceName: AttributeExpressionNameTrackerInterface
))
.ToArray();
var structName = TypeName(typeSymbol);
var @class = $"public readonly struct {structName} : {AttributeExpressionNameTrackerInterface}".CreateScope(TypeContent(typeSymbol, dataMembers, structName));
return new CodeFactory(@class, dataMembers.Where(x => x.IsUnknown).Select(x => x.DDB.DataMember.Type));
}
internal static (string method, string typeName) RootSignature(ITypeSymbol typeSymbol)
{
var typeName = TypeName(typeSymbol);
return ($"public {typeName} {AttributeExpressionNameTrackerMethodName}() => new {typeName}(null, new HashSet<KeyValuePair<string ,string>>());", typeName);
}
}