Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve AttributeValue struct generation #80

Merged
merged 11 commits into from
Oct 18, 2024
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,47 @@ private static string CreateException(in string accessPattern)
return $"throw {Constants.DynamoDBGenerator.ExceptionHelper.NullExceptionMethod}(nameof({accessPattern}));";
}

public static string NotNullIfStatement(this ITypeSymbol typeSymbol, in string accessPattern, in string truthy)
public static IEnumerable<string> NotNullIfStatement(this ITypeSymbol typeSymbol, string accessPattern, string truthy)
{
return NotNullIfStatement(typeSymbol, accessPattern, obj: truthy);
}
public static IEnumerable<string> NotNullIfStatement(this ITypeSymbol typeSymbol, string accessPattern, IEnumerable<string> truthy)
{
return NotNullIfStatement(typeSymbol, accessPattern, obj: truthy);
}

private static IEnumerable<string> NotNullIfStatement(this ITypeSymbol typeSymbol, string accessPattern, object obj)
{
if (Expression(typeSymbol, accessPattern) is not { } expression)
return truthy;

var ifClause = $"if ({expression}) {{ {truthy} }}";
return typeSymbol.NullableAnnotation switch
{
NullableAnnotation.None or NullableAnnotation.Annotated => ifClause,
NullableAnnotation.NotAnnotated => $"{ifClause} else {{ {CreateException(in accessPattern)} }}",
_ => throw new ArgumentOutOfRangeException(typeSymbol.ToDisplayString())
};
if(obj is string single)
yield return single;
else if(obj is IEnumerable<string> truthies)
foreach (var x in truthies)
yield return x;
else
throw new NotImplementedException($"Method '{nameof(NotNullIfStatement)}' could not determine type '{obj.GetType().Name}'");
}
else
{

var ifClause = obj switch
{
string single => $"if ({expression})".CreateScope(single),
IEnumerable<string> multiple => $"if ({expression})".CreateScope(multiple),
_ => throw new NotImplementedException($"Method '{nameof(NotNullIfStatement)}' could not determine type '{obj.GetType().Name}'")
};
var enumerable = typeSymbol.NullableAnnotation switch
{
NullableAnnotation.None or NullableAnnotation.Annotated => ifClause,
NullableAnnotation.NotAnnotated => ifClause.Concat("else".CreateScope(CreateException(in accessPattern))),
_ => throw new ArgumentOutOfRangeException(typeSymbol.ToDisplayString())
};

foreach (var element in enumerable)
yield return element;
}

}


Expand Down Expand Up @@ -72,4 +101,4 @@ public static string NotNullIfStatement(this ITypeSymbol typeSymbol, in string a
}


}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,56 @@ namespace DynamoDBGenerator.SourceGenerator.Extensions;

public static class StringExtensions
{
public static string ToCamelCaseFromPascal(this string str, [System.Runtime.CompilerServices.CallerMemberName] string? memberName = null)
{
return ToCamelCaseFromPascal(str.AsSpan(), memberName).ToString();
}

private static readonly IDictionary<int, string> IndentCache = new Dictionary<int, string>
public static string ToPrivateFieldFromPascal(this string str, [System.Runtime.CompilerServices.CallerMemberName] string? memberName = null)
{
{0, ""},
{1, " "},
{2, " "},
{3, " "},
{4, " "}
};

private static string Indent(int level)
return ToPrivateFieldFromPascal(str.AsSpan(), memberName).ToString();
}

public static ReadOnlySpan<char> ToPrivateFieldFromPascal(this ReadOnlySpan<char> span, [System.Runtime.CompilerServices.CallerMemberName] string? memberName = null)
{
if (IndentCache.TryGetValue(level, out var indent)) return indent;
if (span.Length is 0)
throw new ArgumentException($"Null or Empty string was provided from '{memberName}'");

indent = new string(' ', level * 4);
IndentCache[level] = indent;
var array = new char[span.Length + 1];

return indent;
array[0] = '_';
array[1] = Char.ToLowerInvariant(span[0]);

// Skip first element since we handled it manually.
for (var i = 1; i < span.Length; i++)
array[i + 1] = span[i];

return array;
}
public static IEnumerable<string> CreateScope(this string header, IEnumerable<string> content, int indentLevel)
public static ReadOnlySpan<char> ToCamelCaseFromPascal(this ReadOnlySpan<char> span, [System.Runtime.CompilerServices.CallerMemberName] string? memberName = null)
{
var indent = Indent(indentLevel);
if (span.Length is 0)
throw new ArgumentException($"Null or Empty string was provided from '{memberName}'");

yield return $"{indent}{header}";
yield return string.Intern($"{indent}{{");
if (char.IsLower(span[0]))
return span;

foreach (var s in content)
yield return $"{Indent(indentLevel + 1)}{s}";
var array = new char[span.Length];

yield return string.Intern($"{indent}}}");
array[0] = Char.ToLowerInvariant(span[0]);

// Skip first element since we handled it manually.
for (var i = 1; i < span.Length; i++)
array[i] = span[i];

return array;
}

public static IEnumerable<string> ScopeTo(this IEnumerable<string> content, string header)
{
return CreateScope(header, content);
}

public static IEnumerable<string> CreateScope(this string header, IEnumerable<string> content)
{
yield return header;
Expand All @@ -53,7 +72,7 @@ public static IEnumerable<string> CreateScope(this string header, string content

yield return "}";
}

public static IEnumerable<string> CreateScope(this string header, string content, string second)
{
yield return header;
Expand Down Expand Up @@ -81,4 +100,4 @@ public static string ToAlphaNumericMethodName(this string txt)

return new string(arr, 0, index);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,17 @@ internal static IEnumerable<string> CreateClasses(IEnumerable<DynamoDBMarshaller
}
private static IEnumerable<string> TypeContent(
ITypeSymbol typeSymbol,
(bool IsUnknown, DynamoDbDataMember DDB, string IfBranchAlias, string DbRef, string NameRef, string AttributeReference, string AttributeInterfaceName)[] dataMembers,
(bool IsUnknown, DynamoDbDataMember DDB, string DbRef, string AttributeReference, string AttributeInterfaceName)[] dataMembers,
string structName)
{
const string self = "_self";
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.NameRef} = new (() => new {x.AttributeReference}({ternaryExpressionName}, {ConstructorSetName}));"
: $"{x.NameRef} = new (() => {ternaryExpressionName});";
? $"{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.""));");
Expand All @@ -46,13 +46,13 @@ private static IEnumerable<string> TypeContent(
{
if (fieldDeclaration.IsUnknown)
{
yield return $"private readonly Lazy<{fieldDeclaration.AttributeReference}> {fieldDeclaration.NameRef};";
yield return $"public {fieldDeclaration.AttributeReference} {fieldDeclaration.DDB.DataMember.Name} => {fieldDeclaration.NameRef}.Value;";
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.NameRef};";
yield return $"public string {fieldDeclaration.DDB.DataMember.Name} => {fieldDeclaration.NameRef}.Value;";
yield return $"private readonly Lazy<string> {fieldDeclaration.DDB.DataMember.NameAsPrivateField};";
yield return $"public string {fieldDeclaration.DDB.DataMember.Name} => {fieldDeclaration.DDB.DataMember.NameAsPrivateField}.Value;";

}
}
Expand All @@ -69,19 +69,20 @@ private static IEnumerable<string> TypeContent(
yield return $"public override string ToString() => {self}.Value;";
}

private static IEnumerable<string> YieldSelector((bool IsUnknown, DynamoDbDataMember DDB, string IfBranchAlias, string DbRef, string NameRef, string AttributeReference, string AttributeInterfaceName) x)
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 {x.IfBranchAlias} && {SetFieldName}.Add({x.IfBranchAlias}))"
.CreateScope($"yield return {x.IfBranchAlias};")
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.NameRef}.IsValueCreated)".CreateScope(scope);
return $"if ({x.DDB.DataMember.NameAsPrivateField}.IsValueCreated)".CreateScope(scope);
}
else
{
return $@"if ({x.NameRef}.IsValueCreated && new KeyValuePair<string, string>(""{x.DbRef}"", ""{x.DDB.AttributeName}"") is var {x.IfBranchAlias} && {SetFieldName}.Add({x.IfBranchAlias}))".CreateScope($"yield return {x.IfBranchAlias};");
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};");
}
}

Expand All @@ -91,9 +92,7 @@ private static CodeFactory CreateStruct(ITypeSymbol typeSymbol, Func<ITypeSymbol
.Select(x => (
IsUnknown: !options.IsConvertable(x.DataMember.Type) && x.DataMember.Type.TypeIdentifier() is UnknownType,
DDB: x,
IfBranchAlias: $"__{x.DataMember.Name}__",
DbRef: $"#{x.AttributeName}",
NameRef: $"_{x.DataMember.Name}NameRef",
AttributeReference: TypeName(x.DataMember.Type),
AttributeInterfaceName: AttributeExpressionNameTrackerInterface
))
Expand Down
Loading
Loading