Skip to content

Commit f3e4ddb

Browse files
committed
Geração básica de propriedades
1 parent a7deedd commit f3e4ddb

16 files changed

Lines changed: 271 additions & 102 deletions

File tree

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using RoyalCode.SmartSelector.Demo.Entities.Library;
2+
3+
namespace RoyalCode.SmartSelector.Demo.Details.Library;
4+
5+
[AutoSelect<Book>, AutoProperties(Exclude = [ nameof(Book.Sku) ])]
6+
public partial class BookDetails
7+
{
8+
public Guid Id { get; set; }
9+
}
10+
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
namespace RoyalCode.SmartSelector.Demo.Entities.Library;
2+
3+
public class Book : Entity<Guid>
4+
{
5+
public string Title { get; set; }
6+
7+
public string Author { get; set; }
8+
9+
public DateTime PublishedDate { get; set; }
10+
11+
public string ISBN { get; set; }
12+
13+
public decimal Price { get; set; }
14+
15+
public bool InStock { get; set; }
16+
17+
public string? Sku { get; set; }
18+
19+
public Shelf Shelf { get; set; }
20+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace RoyalCode.SmartSelector.Demo.Entities.Library;
2+
3+
public class Shelf : Entity<Guid>
4+
{
5+
public string Location { get; set; }
6+
7+
public List<Book> Books { get; set; } = new();
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+

2+
namespace RoyalCode.SmartSelector.Demo.Details.Library;
3+
4+
public partial class BookDetails
5+
{
6+
public string Title { get; set; }
7+
8+
public string Author { get; set; }
9+
10+
public DateTime PublishedDate { get; set; }
11+
12+
public string ISBN { get; set; }
13+
14+
public decimal Price { get; set; }
15+
16+
public bool InStock { get; set; }
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using RoyalCode.SmartSelector.Demo.Entities.Library;
2+
using System.Linq.Expressions;
3+
4+
namespace RoyalCode.SmartSelector.Demo.Details.Library;
5+
6+
public partial class BookDetails
7+
{
8+
private static Func<Book, BookDetails> selectBookFunc;
9+
10+
public static Expression<Func<Book, BookDetails>> SelectBookExpression { get; } = a => new BookDetails
11+
{
12+
Id = a.Id
13+
};
14+
15+
public static BookDetails From(Book book) => (selectBookFunc ??= SelectBookExpression.Compile())(book);
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using RoyalCode.SmartSelector.Demo.Entities.Library;
2+
3+
namespace RoyalCode.SmartSelector.Demo.Details.Library;
4+
5+
public static class BookDetails_Extensions
6+
{
7+
public static IQueryable<BookDetails> SelectBookDetails(this IQueryable<Book> query)
8+
{
9+
return query.Select(BookDetails.SelectBookExpression);
10+
}
11+
12+
public static IEnumerable<BookDetails> SelectBookDetails(this IEnumerable<Book> enumerable)
13+
{
14+
return enumerable.Select(BookDetails.From);
15+
}
16+
17+
public static BookDetails ToBookDetails(this Book book) => BookDetails.From(book);
18+
}

src/RoyalCode.SmartSelector.Demo/RoyalCode.SmartSelector.Demo.csproj

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,9 @@
99
<ItemGroup>
1010
<ProjectReference Include="..\RoyalCode.SmartSelector\RoyalCode.SmartSelector.csproj" />
1111

12-
<ProjectReference Include="..\RoyalCode.SmartSelector.Generators\RoyalCode.SmartSelector.Generators.csproj"
13-
OutputItemType="Analyzer"
14-
ReferenceOutputAssembly="false" />
12+
<ProjectReference Include="..\RoyalCode.SmartSelector.Generators\RoyalCode.SmartSelector.Generators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
1513

16-
<PackageReference Include="RoyalCode.Extensions.SourceGenerator" Version="$(ExtSrcGenVer)"
17-
PrivateAssets="all"
18-
ReferenceOutputAssembly="false" />
14+
<PackageReference Include="RoyalCode.Extensions.SourceGenerator" Version="$(ExtSrcGenVer)" PrivateAssets="all" ReferenceOutputAssembly="false" />
1915
</ItemGroup>
2016
<ItemGroup>
2117
<Analyzer Include="$(NuGetPackageRoot)royalcode.extensions.sourcegenerator\$(ExtSrcGenVer)\lib\netstandard2.0\RoyalCode.Extensions.SourceGenerator.dll" />

src/RoyalCode.SmartSelector.Generators/Generators/AttributeSyntaxExtensions.cs renamed to src/RoyalCode.SmartSelector.Generators/Extensions/AttributeSyntaxExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using Microsoft.CodeAnalysis.CSharp.Syntax;
22
using Microsoft.CodeAnalysis;
33

4-
namespace RoyalCode.SmartSelector.Generators.Generators;
4+
namespace System;
55

66
internal static class AttributeSyntaxExtensions
77
{
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using Microsoft.CodeAnalysis;
2+
3+
namespace System;
4+
5+
internal static class LocalExtensions
6+
{
7+
public static TypeDescriptor CreateTypeDescriptor(this ITypeSymbol typeSymbol)
8+
{
9+
string text = typeSymbol.ToString();
10+
bool flag = false;
11+
INamedTypeSymbol? namedTypeSymbol = typeSymbol as INamedTypeSymbol;
12+
if (text[text.Length - 1] == '?')
13+
{
14+
flag = namedTypeSymbol != null && namedTypeSymbol.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T;
15+
}
16+
17+
string[] namespaces = typeSymbol.GetNamespaces().ToArray();
18+
if (namedTypeSymbol != null)
19+
{
20+
if (flag && namedTypeSymbol.TypeArguments[0] is INamedTypeSymbol symbol)
21+
{
22+
text = symbol.GetName() + "?";
23+
}
24+
else if (namedTypeSymbol.SpecialType == SpecialType.None)
25+
{
26+
text = namedTypeSymbol.GetName();
27+
}
28+
else
29+
{
30+
text = namedTypeSymbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat);
31+
}
32+
}
33+
34+
return new TypeDescriptor(text, namespaces, typeSymbol, flag);
35+
}
36+
}

src/RoyalCode.SmartSelector.Generators/Generators/AutoPropertyGenerator.cs renamed to src/RoyalCode.SmartSelector.Generators/Generators/AutoPropertiesGenerator.cs

Lines changed: 96 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,56 +3,61 @@
33

44
namespace RoyalCode.SmartSelector.Generators.Generators;
55

6-
internal static class AutoPropertyGenerator
6+
internal static class AutoPropertiesGenerator
77
{
88
internal static MatchOptions MatchOptions { get; } = new()
99
{
1010
OriginPropertiesRetriever = new AutoPropertyOriginPropertiesRetriever(),
1111
//TargetPropertiesRetriever = new AutoPropertyTargetPropertiesRetriever(),
1212
};
1313

14-
internal static AutoPropertyInformation CreateInformation(
14+
internal static AutoPropertiesInformation CreateInformation(
1515
TypeDescriptor modelType,
1616
TypeDescriptor fromType,
1717
AttributeSyntax properties)
1818
{
1919
// collect excluded property names using extension helpers
2020
var excluded = new HashSet<string>(StringComparer.Ordinal);
2121

22-
foreach (var name in properties.GetConstructorStringSet())
23-
excluded.Add(name);
22+
// removido por hora
23+
////foreach (var name in properties.GetConstructorStringSet())
24+
//// excluded.Add(name);
2425

2526
foreach (var name in properties.GetNamedArgumentStringSet("Exclude"))
2627
excluded.Add(name);
2728

2829
return CreateInformation(modelType, fromType, excluded);
2930
}
3031

31-
internal static AutoPropertyInformation CreateInformation(
32+
internal static AutoPropertiesInformation CreateInformation(
3233
TypeDescriptor modelType,
3334
ITypeSymbol fromType,
3435
AttributeData autoPropertyAttribute)
3536
{
36-
// obtém excluded do ctor ou propriedade Exclude
3737
var excluded = new HashSet<string>(StringComparer.Ordinal);
38-
foreach (var name in autoPropertyAttribute.ConstructorArguments)
39-
if (name.Kind == TypedConstantKind.Array && name.Values != null)
40-
foreach (var v in name.Values)
41-
if (v.Value is string s)
42-
excluded.Add(s);
38+
39+
// removido por hora
40+
////// obtém excluded do ctor ou propriedade Exclude
41+
////foreach (var name in autoPropertyAttribute.ConstructorArguments)
42+
//// if (name.Kind == TypedConstantKind.Array && name.Values != null)
43+
//// foreach (var v in name.Values)
44+
//// if (v.Value is string s)
45+
//// excluded.Add(s);
46+
47+
// obtém Exclude de NamedArguments
4348
foreach (var namedArg in autoPropertyAttribute.NamedArguments)
4449
if (namedArg.Key == "Exclude" && namedArg.Value.Kind == TypedConstantKind.Array && namedArg.Value.Values != null)
4550
foreach (var v in namedArg.Value.Values)
4651
if (v.Value is string s)
4752
excluded.Add(s);
4853

4954
// gera o TypeDescriptor do fromType
50-
var fromTypeDescriptor = TypeDescriptor.Create(fromType);
55+
var fromTypeDescriptor = fromType.CreateTypeDescriptor();
5156

5257
return CreateInformation(modelType, fromTypeDescriptor, excluded);
5358
}
5459

55-
internal static AutoPropertyInformation CreateInformation(
60+
internal static AutoPropertiesInformation CreateInformation(
5661
TypeDescriptor modelType,
5762
TypeDescriptor fromType,
5863
HashSet<string> excluded)
@@ -62,18 +67,82 @@ internal static AutoPropertyInformation CreateInformation(
6267
excluded.Add(p.Name);
6368

6469
var sourceProps = fromType.CreateProperties(p => p.GetMethod is not null);
70+
71+
// filtra propriedades do source,
72+
// remove propriedades que estão na lista de excluídas,
73+
// removendo o que não for tipo primitivo, string, decimal, DateTime,
74+
// enum ou nullable desses tipos, além de coleções de tipos primitivos,
75+
// aceita structs também.
76+
sourceProps = sourceProps
77+
.Where(p => !excluded.Contains(p.Name))
78+
.Where(IsSupportedType)
79+
.ToArray();
80+
6581
var generated = new List<PropertyDescriptor>();
6682
foreach (var p in sourceProps)
6783
{
68-
if (excluded.Contains(p.Name))
69-
continue;
7084
generated.Add(new PropertyDescriptor(p.Type, p.Name, p.Symbol));
7185
}
7286

73-
return new AutoPropertyInformation(modelType, [.. generated]);
87+
return new AutoPropertiesInformation(modelType, [.. generated]);
7488
}
7589

76-
internal static void Generate(AutoPropertyInformation propertiesInfo, SourceProductionContext context)
90+
private static readonly HashSet<string> SupportedPrimitiveTypes = new(StringComparer.Ordinal)
91+
{
92+
"bool",
93+
"Boolean",
94+
"string", "char",
95+
"String", "Char",
96+
"byte", "short", "int", "long", "float", "double", "decimal",
97+
"Byte", "Int16", "Int32", "Int64", "Single", "Double", "Decimal",
98+
"sbyte", "ushort", "uint", "ulong",
99+
"SByte", "UInt16", "UInt32", "UInt64",
100+
"DateTime", "DateTime?",
101+
};
102+
103+
private static bool IsSupportedType(PropertyDescriptor descriptor)
104+
{
105+
var type = descriptor.Type;
106+
var typeName = type.Name;
107+
108+
if (SupportedPrimitiveTypes.Contains(typeName))
109+
return true;
110+
111+
if (!type.HasNamedTypeSymbol(out var namedType))
112+
return false;
113+
114+
// se tem symbol, verifica se é enum
115+
if (namedType.TypeKind == TypeKind.Enum)
116+
return true;
117+
118+
// struct é considerado um value object, não é um tipo complexo e deve ser aceito
119+
if (namedType.TypeKind == TypeKind.Struct)
120+
return true;
121+
122+
// se for genérico, verifica se coleção suportada
123+
if (namedType.IsGenericType)
124+
{
125+
// verifica se namedType implementa ou herda IEnumerable<>
126+
if (!namedType.AllInterfaces.Any(i =>
127+
i.OriginalDefinition.SpecialType == SpecialType.System_Collections_Generic_IEnumerable_T))
128+
{
129+
return false;
130+
}
131+
132+
var arg = namedType.TypeArguments.FirstOrDefault();
133+
if (arg != null)
134+
{
135+
var argType = arg.CreateTypeDescriptor();
136+
return SupportedPrimitiveTypes.Contains(argType.Name)
137+
|| arg.TypeKind == TypeKind.Enum
138+
|| arg.TypeKind == TypeKind.Struct;
139+
}
140+
}
141+
142+
return false;
143+
}
144+
145+
internal static void Generate(AutoPropertiesInformation propertiesInfo, SourceProductionContext context)
77146
{
78147
// gera o código de propriedades automáticas em uma classe partial
79148

@@ -109,11 +178,15 @@ internal static void Generate(AutoPropertyInformation propertiesInfo, SourceProd
109178
// 2 - criação das propriedades
110179
foreach (var p in properties)
111180
{
112-
var prop = new PropertyGenerator(p.Type, p.Name);
113-
181+
var propertyType = p.Type.HasNamedTypeSymbol(out var typeSymbol)
182+
? typeSymbol.CreateTypeDescriptor()
183+
: p.Type;
184+
185+
var prop = new PropertyGenerator(propertyType, p.Name);
186+
114187
// 2.1 modificadores
115188
prop.Modifiers.Public();
116-
189+
117190
// 2.3 adiciona a propriedade na classe
118191
partialClass.Properties.Add(prop);
119192
}
@@ -157,7 +230,7 @@ public IReadOnlyList<PropertyDescriptor> GetProperties(TypeDescriptor origin)
157230
return MatchOptions.GetOriginProperties(origin);
158231

159232
// cria a informação
160-
var info = AutoPropertyGenerator.CreateInformation(origin, fromType, autoSelectAttribute);
233+
var info = AutoPropertiesGenerator.CreateInformation(origin, fromType, autoSelectAttribute);
161234

162235
// pega as propriedades da origem mais as da informação
163236
return [.. MatchOptions.GetOriginProperties(origin), .. info.Properties];
@@ -175,12 +248,12 @@ public IReadOnlyList<PropertyDescriptor> GetProperties(TypeDescriptor origin)
175248
return MatchOptions.GetOriginProperties(origin);
176249

177250
// cria a informação
178-
var info = AutoPropertyGenerator.CreateInformation(origin, fromType, autoPropertiesAttribute);
251+
var info = AutoPropertiesGenerator.CreateInformation(origin, fromType, autoPropertiesAttribute);
179252

180253
// pega as propriedades da origem mais as da informação
181254
return [.. MatchOptions.GetOriginProperties(origin), .. info.Properties];
182255
}
183-
256+
184257
return MatchOptions.GetOriginProperties(origin);
185258
}
186259
}

0 commit comments

Comments
 (0)