Skip to content

Commit a8286a9

Browse files
CopilotDreamescaper
andcommitted
Address PR review: multiline collection expr, switch expr, extract methods, README
Co-authored-by: Dreamescaper <17177729+Dreamescaper@users.noreply.github.com> Agent-Logs-Url: https://github.com/Dreamescaper/ServiceScan.SourceGenerator/sessions/a26f24d8-be32-4167-9a0b-2eaed9dd023e
1 parent ac13efe commit a8286a9

4 files changed

Lines changed: 131 additions & 76 deletions

File tree

README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,31 @@ public static partial class ModelBuilderExtensions
145145
}
146146
```
147147

148+
### Get all matched types as a collection
149+
150+
When `Handler` is omitted and the method returns `Type[]` or `IEnumerable<Type>`, `ScanForTypes` returns a collection of matched types:
151+
```csharp
152+
public static partial class TypeDiscovery
153+
{
154+
[ScanForTypes(AssignableTo = typeof(IService))]
155+
public static partial Type[] GetAllServiceTypes();
156+
}
157+
```
158+
159+
### Map matched types to a custom result type
160+
161+
When the method returns `TResponse[]` or `IEnumerable<TResponse>`, specify a `Handler` that maps each found type to `TResponse`:
162+
```csharp
163+
public static partial class TypeDiscovery
164+
{
165+
[ScanForTypes(AssignableTo = typeof(IService), Handler = nameof(Describe))]
166+
public static partial ServiceDescriptor[] GetServiceDescriptors();
167+
168+
private static ServiceDescriptor Describe<T>() where T : IService
169+
=> ServiceDescriptor.Transient(typeof(IService), typeof(T));
170+
}
171+
```
172+
148173

149174

150175
## Parameters

ServiceScan.SourceGenerator.Tests/CustomHandlerTests.cs

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1383,7 +1383,10 @@ public static partial class ServicesExtensions
13831383
{
13841384
public static partial global::System.Type[] GetServiceTypes()
13851385
{
1386-
return [typeof(global::GeneratorTests.MyService1), typeof(global::GeneratorTests.MyService2)];
1386+
return [
1387+
typeof(global::GeneratorTests.MyService1),
1388+
typeof(global::GeneratorTests.MyService2)
1389+
];
13871390
}
13881391
}
13891392
""";
@@ -1430,7 +1433,10 @@ public static partial class ServicesExtensions
14301433
{
14311434
public static partial global::System.Collections.Generic.IEnumerable<global::System.Type> GetServiceTypes()
14321435
{
1433-
return [typeof(global::GeneratorTests.MyService1), typeof(global::GeneratorTests.MyService2)];
1436+
return [
1437+
typeof(global::GeneratorTests.MyService1),
1438+
typeof(global::GeneratorTests.MyService2)
1439+
];
14341440
}
14351441
}
14361442
""";
@@ -1482,7 +1488,10 @@ public static partial class ServicesExtensions
14821488
{
14831489
public static partial global::GeneratorTests.ServiceInfo[] GetServiceInfos()
14841490
{
1485-
return [GetServiceInfo<global::GeneratorTests.MyService1>(), GetServiceInfo<global::GeneratorTests.MyService2>()];
1491+
return [
1492+
GetServiceInfo<global::GeneratorTests.MyService1>(),
1493+
GetServiceInfo<global::GeneratorTests.MyService2>()
1494+
];
14861495
}
14871496
}
14881497
""";
@@ -1535,7 +1544,10 @@ public static partial class ServicesExtensions
15351544
{
15361545
public static partial global::System.Collections.Generic.IEnumerable<global::GeneratorTests.ServiceInfo> GetServiceInfos()
15371546
{
1538-
return [GetServiceInfo<global::GeneratorTests.MyService1>(), GetServiceInfo<global::GeneratorTests.MyService2>()];
1547+
return [
1548+
GetServiceInfo<global::GeneratorTests.MyService1>(),
1549+
GetServiceInfo<global::GeneratorTests.MyService2>()
1550+
];
15391551
}
15401552
}
15411553
""";
@@ -1583,7 +1595,10 @@ public static partial class ServicesExtensions
15831595
{
15841596
public static partial global::System.Type[] GetServiceTypes()
15851597
{
1586-
return [typeof(global::GeneratorTests.MyService1), typeof(global::GeneratorTests.MyService2)];
1598+
return [
1599+
typeof(global::GeneratorTests.MyService1),
1600+
typeof(global::GeneratorTests.MyService2)
1601+
];
15871602
}
15881603
}
15891604
""";

ServiceScan.SourceGenerator/DependencyInjectionGenerator.FindServicesToRegister.cs

Lines changed: 78 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -36,74 +36,13 @@ private static DiagnosticModel<MethodImplementationModel> FindServicesToRegister
3636
{
3737
typesFound = true;
3838

39-
var implementationTypeName = implementationType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
40-
4139
if (method.ReturnTypeIsCollection)
42-
{
43-
if (attribute.CustomHandler == null)
44-
{
45-
// Case 1: no handler, return typeof(T) expressions
46-
collectionItems.Add($"typeof({implementationTypeName})");
47-
}
48-
else
49-
{
50-
// Case 2: handler maps T -> TResponse, generate handler invocation expressions
51-
var arguments = string.Join(", ", method.Parameters.Select(p => p.Name));
52-
53-
if (attribute.CustomHandlerMethodTypeParametersCount > 1 && matchedTypes != null)
54-
{
55-
foreach (var matchedType in matchedTypes)
56-
{
57-
var typeArguments = string.Join(", ", new[] { implementationTypeName }
58-
.Concat(matchedType.TypeArguments.Select(a => a.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat))));
59-
60-
if (attribute.CustomHandlerType == CustomHandlerType.Method)
61-
collectionItems.Add($"{attribute.CustomHandler}<{typeArguments}>({arguments})");
62-
else
63-
collectionItems.Add($"{implementationTypeName}.{attribute.CustomHandler}({arguments})");
64-
}
65-
}
66-
else
67-
{
68-
if (attribute.CustomHandlerType == CustomHandlerType.Method)
69-
collectionItems.Add($"{attribute.CustomHandler}<{implementationTypeName}>({arguments})");
70-
else
71-
collectionItems.Add($"{implementationTypeName}.{attribute.CustomHandler}({arguments})");
72-
}
73-
}
74-
}
40+
AddCollectionItems(implementationType, matchedTypes, attribute, method, collectionItems);
7541
else if (attribute.CustomHandler != null)
76-
{
77-
// If CustomHandler method has multiple type parameters, which are resolvable from the first one - we try to provide them.
78-
// e.g. ApplyConfiguration<T, TEntity>(ModelBuilder modelBuilder) where T : IEntityTypeConfiguration<TEntity>
79-
if (attribute.CustomHandlerMethodTypeParametersCount > 1 && matchedTypes != null)
80-
{
81-
foreach (var matchedType in matchedTypes)
82-
{
83-
EquatableArray<string> typeArguments =
84-
[
85-
implementationTypeName,
86-
.. matchedType.TypeArguments.Select(a => a.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat))
87-
];
88-
89-
customHandlers.Add(new CustomHandlerModel(
90-
attribute.CustomHandlerType.Value,
91-
attribute.CustomHandler,
92-
implementationTypeName,
93-
typeArguments));
94-
}
95-
}
96-
else
97-
{
98-
customHandlers.Add(new CustomHandlerModel(
99-
attribute.CustomHandlerType.Value,
100-
attribute.CustomHandler,
101-
implementationTypeName,
102-
[implementationTypeName]));
103-
}
104-
}
42+
AddCustomHandlerItems(implementationType, matchedTypes, attribute, customHandlers);
10543
else
10644
{
45+
var implementationTypeName = implementationType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
10746
var serviceTypes = (attribute.AsSelf, attribute.AsImplementedInterfaces) switch
10847
{
10948
(true, true) => [implementationType, .. GetSuitableInterfaces(implementationType)],
@@ -158,6 +97,81 @@ .. matchedType.TypeArguments.Select(a => a.ToDisplayString(SymbolDisplayFormat.F
15897
return new(diagnostic, implementationModel);
15998
}
16099

100+
private static void AddCollectionItems(
101+
INamedTypeSymbol implementationType,
102+
IEnumerable<INamedTypeSymbol>? matchedTypes,
103+
AttributeModel attribute,
104+
MethodModel method,
105+
List<string> collectionItems)
106+
{
107+
var implementationTypeName = implementationType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
108+
109+
if (attribute.CustomHandler == null)
110+
{
111+
collectionItems.Add($"typeof({implementationTypeName})");
112+
}
113+
else
114+
{
115+
var arguments = string.Join(", ", method.Parameters.Select(p => p.Name));
116+
117+
if (attribute.CustomHandlerMethodTypeParametersCount > 1 && matchedTypes != null)
118+
{
119+
foreach (var matchedType in matchedTypes)
120+
{
121+
var typeArguments = string.Join(", ", new[] { implementationTypeName }
122+
.Concat(matchedType.TypeArguments.Select(a => a.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat))));
123+
124+
if (attribute.CustomHandlerType == CustomHandlerType.Method)
125+
collectionItems.Add($"{attribute.CustomHandler}<{typeArguments}>({arguments})");
126+
else
127+
collectionItems.Add($"{implementationTypeName}.{attribute.CustomHandler}({arguments})");
128+
}
129+
}
130+
else
131+
{
132+
if (attribute.CustomHandlerType == CustomHandlerType.Method)
133+
collectionItems.Add($"{attribute.CustomHandler}<{implementationTypeName}>({arguments})");
134+
else
135+
collectionItems.Add($"{implementationTypeName}.{attribute.CustomHandler}({arguments})");
136+
}
137+
}
138+
}
139+
140+
private static void AddCustomHandlerItems(
141+
INamedTypeSymbol implementationType,
142+
IEnumerable<INamedTypeSymbol>? matchedTypes,
143+
AttributeModel attribute,
144+
List<CustomHandlerModel> customHandlers)
145+
{
146+
var implementationTypeName = implementationType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
147+
148+
if (attribute.CustomHandlerMethodTypeParametersCount > 1 && matchedTypes != null)
149+
{
150+
foreach (var matchedType in matchedTypes)
151+
{
152+
EquatableArray<string> typeArguments =
153+
[
154+
implementationTypeName,
155+
.. matchedType.TypeArguments.Select(a => a.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat))
156+
];
157+
158+
customHandlers.Add(new CustomHandlerModel(
159+
attribute.CustomHandlerType.Value,
160+
attribute.CustomHandler,
161+
implementationTypeName,
162+
typeArguments));
163+
}
164+
}
165+
else
166+
{
167+
customHandlers.Add(new CustomHandlerModel(
168+
attribute.CustomHandlerType.Value,
169+
attribute.CustomHandler,
170+
implementationTypeName,
171+
[implementationTypeName]));
172+
}
173+
}
174+
161175
private static IEnumerable<INamedTypeSymbol> GetSuitableInterfaces(ITypeSymbol type)
162176
{
163177
return type.AllInterfaces.Where(x => !ExcludedInterfaces.Contains(x.ToDisplayString()));

ServiceScan.SourceGenerator/DependencyInjectionGenerator.cs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,12 @@ private static void GenerateSource(SourceProductionContext context, DiagnosticMo
5959
return;
6060

6161
var (method, registrations, customHandling, collectionItems) = src.Model;
62-
string source = registrations.Count > 0
63-
? GenerateRegistrationsSource(method, registrations)
64-
: method.ReturnTypeIsCollection
65-
? GenerateCollectionSource(method, collectionItems)
66-
: GenerateCustomHandlingSource(method, customHandling);
62+
string source = (registrations.Count, method.ReturnTypeIsCollection) switch
63+
{
64+
( > 0, _) => GenerateRegistrationsSource(method, registrations),
65+
(_, true) => GenerateCollectionSource(method, collectionItems),
66+
_ => GenerateCustomHandlingSource(method, customHandling)
67+
};
6768

6869
source = source.ReplaceLineEndings();
6970

@@ -135,8 +136,8 @@ private static string GenerateCollectionSource(MethodModel method, EquatableArra
135136
var parameters = string.Join(",", method.Parameters.Select((p, i) =>
136137
$"{(i == 0 && method.IsExtensionMethod ? "this" : "")} {p.Type} {p.Name}"));
137138

138-
var items = string.Join(", ", collectionItems);
139-
var methodBody = $"return [{items}];";
139+
var itemsCode = string.Join(",\n", collectionItems.Select(item => $" {item}"));
140+
var methodBody = $"return [\n{itemsCode}\n ];";
140141

141142
var source = $$"""
142143
{{namespaceDeclaration}}

0 commit comments

Comments
 (0)