Skip to content

Commit d1c0dcf

Browse files
committed
select sub entities completed.
1 parent 6a26b74 commit d1c0dcf

13 files changed

Lines changed: 295 additions & 50 deletions

File tree

src/RoyalCode.SmartSelector.Demo/Details/Blogs/PostDetails.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@ public partial class PostDetails
1010
public string Title { get; set; }
1111
public string Content { get; set; }
1212
public AuthorDetails Author { get; set; }
13-
//public ICollection<CommentDetails> Comments { get; set; } = [];
13+
public ICollection<CommentDetails> Comments { get; set; } = [];
1414
}

src/RoyalCode.SmartSelector.Demo/Generated/RoyalCode.SmartSelector.Generators/RoyalCode.SmartSelector.Generators.IncrementalGenerator/PostDetails.g.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@ public partial class PostDetails
1414
Author = new AuthorDetails
1515
{
1616
Name = a.Author.Name
17-
}
17+
},
18+
Comments = a.Comments.Select(b => new CommentDetails
19+
{
20+
Content = b.Content,
21+
AuthorName = b.Author.Name
22+
}).ToList()
1823
};
1924

2025
public static PostDetails From(Post post) => (selectPostFunc ??= SelectPostExpression.Compile())(post);

src/RoyalCode.SmartSelector.Generators/Extensions/ExtensionMethods.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,7 @@ public static void Each<T>(this IEnumerable<T> values, Action<T> action)
445445
action(v);
446446
}
447447

448+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
448449
public static string ToLowerCamelCase(this string value)
449450
{
450451
if (string.IsNullOrEmpty(value))
@@ -555,6 +556,7 @@ private static bool TryGetEnumerableGenericTypeCore(ITypeSymbol symbol, out ITyp
555556
return false;
556557
}
557558

559+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
558560
public static StringBuilder AppendPropertyPath(this StringBuilder sb, PropertySelection property)
559561
{
560562
property.WritePropertyPath(sb);

src/RoyalCode.SmartSelector.Generators/Models/Descriptors/AssignDescriptor.cs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@ internal class AssignDescriptor : IEquatable<AssignDescriptor>
44
{
55
public AssignType AssignType { get; set; }
66

7-
public bool IsEnumerable { get; set; }
8-
9-
public bool RequireSelect { get; set; }
7+
public bool RequireToList { get; set; }
108

119
public MatchSelection? InnerSelection { get; set; }
1210

@@ -18,9 +16,9 @@ public bool Equals(AssignDescriptor other)
1816
if (ReferenceEquals(this, other))
1917
return true;
2018

21-
return AssignType == other.AssignType &&
22-
IsEnumerable == other.IsEnumerable &&
23-
RequireSelect == other.RequireSelect;
19+
return AssignType == other.AssignType &&
20+
RequireToList == other.RequireToList &&
21+
Equals(InnerSelection, other.InnerSelection);
2422
}
2523

2624
public override bool Equals(object? obj)
@@ -35,8 +33,8 @@ public override int GetHashCode()
3533
{
3634
int hashCode = -2066519001;
3735
hashCode = hashCode * -1521134295 + AssignType.GetHashCode();
38-
hashCode = hashCode * -1521134295 + IsEnumerable.GetHashCode();
39-
hashCode = hashCode * -1521134295 + RequireSelect.GetHashCode();
36+
hashCode = hashCode * -1521134295 + RequireToList.GetHashCode();
37+
hashCode = hashCode * -1521134295 + (InnerSelection?.GetHashCode() ?? 0);
4038
return hashCode;
4139
}
4240
}

src/RoyalCode.SmartSelector.Generators/Models/Descriptors/AssignType.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,20 @@ internal enum AssignType
6464
/// </code>
6565
/// </remarks>
6666
NewInstance,
67+
68+
/// <summary>
69+
/// It is required to apply the Select method to the target property type,
70+
/// to transform the elements accordingly.
71+
/// </summary>
72+
/// <remarks>
73+
/// Example:
74+
/// <code>
75+
/// Posts = e.Posts.Select(p => new PostDto
76+
/// {
77+
/// Title = p.Title,
78+
/// Content = p.Content
79+
/// }),
80+
/// </code>
81+
/// </remarks>
82+
Select,
6783
}

src/RoyalCode.SmartSelector.Generators/Models/Descriptors/DirectAssignDescriptorResolver.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using Microsoft.CodeAnalysis;
22
using Microsoft.CodeAnalysis.CSharp;
3-
using RoyalCode.SmartSelector.Generators.Extensions;
43

54
namespace RoyalCode.SmartSelector.Generators.Models.Descriptors;
65

@@ -18,12 +17,9 @@ public bool TryCreateAssignDescriptor(
1817
return false;
1918
}
2019

21-
var isString = leftType.Symbol?.SpecialType == SpecialType.System_String;
22-
2320
descriptor = new AssignDescriptor
2421
{
2522
AssignType = AssignType.Direct,
26-
IsEnumerable = !isString && (leftType.Symbol?.TryGetEnumerableGenericType(out _) ?? false)
2723
};
2824
return true;
2925
}

src/RoyalCode.SmartSelector.Generators/Models/Descriptors/EnumerableAssignDescriptorResolver.cs

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Microsoft.CodeAnalysis;
2+
using Microsoft.CodeAnalysis.CSharp;
23
using RoyalCode.SmartSelector.Generators.Extensions;
34

45
namespace RoyalCode.SmartSelector.Generators.Models.Descriptors;
@@ -22,13 +23,59 @@ public bool TryCreateAssignDescriptor(
2223
TypeDescriptor leftGenericType = TypeDescriptor.Create(leftGenericSymbol!);
2324
TypeDescriptor rightGenericType = TypeDescriptor.Create(rightGenericSymbol!);
2425

25-
AssignDescriptor? genericAssignment = AssignDescriptorFactory.Create(
26-
leftGenericType, rightGenericType, model);
26+
bool requireSelect = false;
27+
AssignDescriptor? genericAssignment = null;
28+
bool requireToList;
2729

28-
if (genericAssignment is not null)
29-
genericAssignment.RequireSelect = true;
30+
// se os tipos genéricos são iguais, não é necessário fazer a conversão
31+
// cas contrário, é necessário fazer a conversão
32+
if (!leftGenericType.Equals(rightGenericType))
33+
{
34+
requireSelect = true;
35+
genericAssignment = AssignDescriptorFactory.Create(leftGenericType, rightGenericType, model);
36+
37+
// se a conversão dos tipos genéricos não for possível, não é possível fazer a conversão desta propriedade
38+
if (genericAssignment is null)
39+
{
40+
descriptor = null;
41+
return false;
42+
}
43+
}
44+
45+
// se o leftType não for um IEnumerable<T>, deve ser assinável por List<T>.
46+
if (leftType.IsEnumerable)
47+
{
48+
requireToList = false;
49+
}
50+
else
51+
{
52+
requireToList = true;
53+
54+
// checa se o leftType symbol pode receber um List<T>
55+
var listType = model.Compilation.GetTypeByMetadataName("System.Collections.Generic.List`1");
56+
if (listType is null)
57+
{
58+
descriptor = null;
59+
return false;
60+
}
61+
62+
var conversion = model.Compilation.ClassifyConversion(
63+
listType,
64+
leftType.Symbol);
65+
66+
if (!conversion.Exists)
67+
{
68+
descriptor = null;
69+
return false;
70+
}
71+
}
3072

31-
descriptor = genericAssignment;
32-
return descriptor is not null;
73+
descriptor = new AssignDescriptor()
74+
{
75+
AssignType = requireSelect ? AssignType.Select : AssignType.Direct,
76+
RequireToList = requireToList,
77+
InnerSelection = genericAssignment?.InnerSelection,
78+
};
79+
return true;
3380
}
3481
}

src/RoyalCode.SmartSelector.Generators/Models/Descriptors/InnerTypeAssignDescriptorResolver.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,6 @@ public bool TryCreateAssignDescriptor(
4444
descriptor = new AssignDescriptor()
4545
{
4646
AssignType = AssignType.NewInstance,
47-
IsEnumerable = false,
48-
RequireSelect = false,
4947
InnerSelection = matchSelection
5048
};
5149

src/RoyalCode.SmartSelector.Generators/Models/Descriptors/TypeDescriptor.cs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,8 @@ public void AddHint(string hint)
141141

142142
public bool IsArray => Name.EndsWith("[]") || Name.EndsWith("[]?");
143143

144+
public bool IsEnumerable => Name.StartsWith("IEnumerable<") || Name.Equals("IEnumerable");
145+
144146
public string UnderlyingType => IsNullable || Name[Name.Length - 1] == '?' ? Name.Substring(0, Name.Length - 1) : Name;
145147

146148
public string ArrayType => Name.EndsWith("[]")
@@ -167,11 +169,20 @@ public IReadOnlyList<PropertyDescriptor> CreateProperties(Func<IPropertySymbol,
167169
if (Symbol is null)
168170
return [];
169171

170-
var properties = new List<PropertyDescriptor>();
172+
var typeSymbols = new List<INamedTypeSymbol>();
171173
var typeSymbol = Symbol;
172174

173-
// Traverse base types to collect all public instance properties
175+
// Collect all base types up to object, including the current type
174176
while (typeSymbol is INamedTypeSymbol namedType)
177+
{
178+
typeSymbols.Insert(0, namedType); // Insert at the beginning to reverse the order
179+
typeSymbol = namedType.BaseType;
180+
}
181+
182+
var properties = new List<PropertyDescriptor>();
183+
184+
// For each type, from base to derived, add its properties in declaration order
185+
foreach (var namedType in typeSymbols)
175186
{
176187
var props = namedType
177188
.GetMembers()
@@ -181,14 +192,12 @@ public IReadOnlyList<PropertyDescriptor> CreateProperties(Func<IPropertySymbol,
181192
.Select(PropertyDescriptor.Create);
182193

183194
properties.AddRange(props);
184-
185-
typeSymbol = namedType.BaseType;
186195
}
187196

188197
// Remove duplicates by property name (derived class property hides base class property)
189198
var distinctProperties = properties
190199
.GroupBy(p => p.Name)
191-
.Select(g => g.First())
200+
.Select(g => g.Last()) // Now, the most derived property is last, so take Last()
192201
.ToList();
193202

194203
return distinctProperties;

src/RoyalCode.SmartSelector.Generators/Models/Generators/Commands/SelectLambdaGenerator.cs

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -50,31 +50,27 @@ private static void GeneratePropertyCode(int ident, StringBuilder sb, char param
5050
var assign = new AssignProperties(propMatch.Origin, propMatch.Target!, assignDescriptor.InnerSelection);
5151
assignGenerator(sb, ident, param, assign);
5252

53+
// check ToList
54+
if (assignDescriptor.RequireToList)
55+
{
56+
sb.Append('.').Append("ToList()");
57+
}
58+
5359
sb.Append(',');
5460
}
5561
sb.Length--;
5662
}
5763

5864
private static AssignGenerator GetAssignGenerator(AssignDescriptor assignDescriptor)
5965
{
60-
if (assignDescriptor.IsEnumerable)
61-
{
62-
return assignDescriptor.AssignType switch
63-
{
64-
AssignType.Direct => assignDescriptor.RequireSelect
65-
? AssignSelectDirect
66-
: AssignEnumerable,
67-
_ => AssignEnumerable
68-
};
69-
}
70-
7166
return assignDescriptor.AssignType switch
7267
{
7368
AssignType.Direct => AssignDirect,
7469
AssignType.SimpleCast => AssignCast,
7570
AssignType.NullableTernary => AssignNullableTernary,
7671
AssignType.NullableTernaryCast => AssignNullableTernaryCast,
7772
AssignType.NewInstance => AssignNewInstance,
73+
AssignType.Select => AssignSelect,
7874
_ => AssignDirect
7975
};
8076
}
@@ -103,13 +99,6 @@ private static void AssignNullableTernaryCast(StringBuilder sb, int ident, char
10399
sb.Append(param).Append('.').AppendPropertyPath(assign.Target).Append(".Value : default");
104100
}
105101

106-
private static void AssignEnumerable(StringBuilder sb, int ident, char param, AssignProperties assign)
107-
{
108-
sb.Append("[.. ");
109-
AssignDirect(sb, ident, param, assign);
110-
sb.Append(']');
111-
}
112-
113102
private static void AssignNewInstance(StringBuilder sb, int ident, char param, AssignProperties assign)
114103
{
115104
var inner = assign.InnerSelection;
@@ -126,9 +115,22 @@ private static void AssignNewInstance(StringBuilder sb, int ident, char param, A
126115
sb.AppendLine().Ident(ident).Append('}');
127116
}
128117

129-
private static void AssignSelectDirect(StringBuilder sb, int ident, char param, AssignProperties assign)
118+
private static void AssignSelect(StringBuilder sb, int ident, char param, AssignProperties assign)
130119
{
131-
// TODO
120+
var inner = assign.InnerSelection;
121+
if (inner is null)
122+
throw new ArgumentException("Inner selection is null.", nameof(inner));
123+
124+
sb.Append(param).Append('.').AppendPropertyPath(assign.Target).Append(".Select(");
125+
126+
var nextParam = (char)(param + 1);
127+
128+
sb.Append(nextParam).Append(" => new ").AppendLine(assign.Origin.Type.GenericType)
129+
.Ident(ident).Append('{');
130+
131+
GeneratePropertyCode(ident + 1, sb, nextParam, inner.PropertyMatches);
132+
133+
sb.AppendLine().Ident(ident).Append("})");
132134
}
133135

134136
private delegate void AssignGenerator(StringBuilder sb, int ident, char param, AssignProperties assign);

0 commit comments

Comments
 (0)