Skip to content

Commit 405679a

Browse files
authored
Add collection return support to ScanForTypes attribute (#55)
1 parent d14cace commit 405679a

8 files changed

Lines changed: 541 additions & 61 deletions

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: 331 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1343,4 +1343,335 @@ public class MyService : IService { }
13431343

13441344
Assert.Contains(results.Diagnostics, d => d.Descriptor == DiagnosticDescriptors.CantMixServiceRegistrationsAndServiceHandler);
13451345
}
1346+
1347+
[Fact]
1348+
public void ScanForTypesAttribute_ReturnsTypeArray_WithNoHandler()
1349+
{
1350+
var source = """
1351+
using ServiceScan.SourceGenerator;
1352+
using System;
1353+
1354+
namespace GeneratorTests;
1355+
1356+
public static partial class ServicesExtensions
1357+
{
1358+
[ScanForTypes(AssignableTo = typeof(IService))]
1359+
public static partial Type[] GetServiceTypes();
1360+
}
1361+
""";
1362+
1363+
var services =
1364+
"""
1365+
namespace GeneratorTests;
1366+
1367+
public interface IService { }
1368+
public class MyService1 : IService { }
1369+
public class MyService2 : IService { }
1370+
""";
1371+
1372+
var compilation = CreateCompilation(source, services);
1373+
1374+
var results = CSharpGeneratorDriver
1375+
.Create(_generator)
1376+
.RunGenerators(compilation)
1377+
.GetRunResult();
1378+
1379+
var expected = """
1380+
namespace GeneratorTests;
1381+
1382+
public static partial class ServicesExtensions
1383+
{
1384+
public static partial global::System.Type[] GetServiceTypes()
1385+
{
1386+
return [
1387+
typeof(global::GeneratorTests.MyService1),
1388+
typeof(global::GeneratorTests.MyService2)
1389+
];
1390+
}
1391+
}
1392+
""";
1393+
Assert.Equal(expected, results.GeneratedTrees[2].ToString());
1394+
}
1395+
1396+
[Fact]
1397+
public void ScanForTypesAttribute_ReturnsIEnumerableType_WithNoHandler()
1398+
{
1399+
var source = """
1400+
using ServiceScan.SourceGenerator;
1401+
using System;
1402+
using System.Collections.Generic;
1403+
1404+
namespace GeneratorTests;
1405+
1406+
public static partial class ServicesExtensions
1407+
{
1408+
[ScanForTypes(AssignableTo = typeof(IService))]
1409+
public static partial IEnumerable<Type> GetServiceTypes();
1410+
}
1411+
""";
1412+
1413+
var services =
1414+
"""
1415+
namespace GeneratorTests;
1416+
1417+
public interface IService { }
1418+
public class MyService1 : IService { }
1419+
public class MyService2 : IService { }
1420+
""";
1421+
1422+
var compilation = CreateCompilation(source, services);
1423+
1424+
var results = CSharpGeneratorDriver
1425+
.Create(_generator)
1426+
.RunGenerators(compilation)
1427+
.GetRunResult();
1428+
1429+
var expected = """
1430+
namespace GeneratorTests;
1431+
1432+
public static partial class ServicesExtensions
1433+
{
1434+
public static partial global::System.Collections.Generic.IEnumerable<global::System.Type> GetServiceTypes()
1435+
{
1436+
return [
1437+
typeof(global::GeneratorTests.MyService1),
1438+
typeof(global::GeneratorTests.MyService2)
1439+
];
1440+
}
1441+
}
1442+
""";
1443+
Assert.Equal(expected, results.GeneratedTrees[2].ToString());
1444+
}
1445+
1446+
[Fact]
1447+
public void ScanForTypesAttribute_ReturnsResponseArray_WithHandler()
1448+
{
1449+
var source = """
1450+
using ServiceScan.SourceGenerator;
1451+
1452+
namespace GeneratorTests;
1453+
1454+
public static partial class ServicesExtensions
1455+
{
1456+
[ScanForTypes(AssignableTo = typeof(IService), Handler = nameof(GetServiceInfo))]
1457+
public static partial ServiceInfo[] GetServiceInfos();
1458+
1459+
private static ServiceInfo GetServiceInfo<T>() => new ServiceInfo(typeof(T).Name);
1460+
}
1461+
""";
1462+
1463+
var services =
1464+
"""
1465+
namespace GeneratorTests;
1466+
1467+
public interface IService { }
1468+
public class MyService1 : IService { }
1469+
public class MyService2 : IService { }
1470+
1471+
public class ServiceInfo
1472+
{
1473+
public ServiceInfo(string name) { }
1474+
}
1475+
""";
1476+
1477+
var compilation = CreateCompilation(source, services);
1478+
1479+
var results = CSharpGeneratorDriver
1480+
.Create(_generator)
1481+
.RunGenerators(compilation)
1482+
.GetRunResult();
1483+
1484+
var expected = """
1485+
namespace GeneratorTests;
1486+
1487+
public static partial class ServicesExtensions
1488+
{
1489+
public static partial global::GeneratorTests.ServiceInfo[] GetServiceInfos()
1490+
{
1491+
return [
1492+
GetServiceInfo<global::GeneratorTests.MyService1>(),
1493+
GetServiceInfo<global::GeneratorTests.MyService2>()
1494+
];
1495+
}
1496+
}
1497+
""";
1498+
Assert.Equal(expected, results.GeneratedTrees[2].ToString());
1499+
}
1500+
1501+
[Fact]
1502+
public void ScanForTypesAttribute_ReturnsIEnumerableResponse_WithHandler()
1503+
{
1504+
var source = """
1505+
using ServiceScan.SourceGenerator;
1506+
using System.Collections.Generic;
1507+
1508+
namespace GeneratorTests;
1509+
1510+
public static partial class ServicesExtensions
1511+
{
1512+
[ScanForTypes(AssignableTo = typeof(IService), Handler = nameof(GetServiceInfo))]
1513+
public static partial IEnumerable<ServiceInfo> GetServiceInfos();
1514+
1515+
private static ServiceInfo GetServiceInfo<T>() => new ServiceInfo(typeof(T).Name);
1516+
}
1517+
""";
1518+
1519+
var services =
1520+
"""
1521+
namespace GeneratorTests;
1522+
1523+
public interface IService { }
1524+
public class MyService1 : IService { }
1525+
public class MyService2 : IService { }
1526+
1527+
public class ServiceInfo
1528+
{
1529+
public ServiceInfo(string name) { }
1530+
}
1531+
""";
1532+
1533+
var compilation = CreateCompilation(source, services);
1534+
1535+
var results = CSharpGeneratorDriver
1536+
.Create(_generator)
1537+
.RunGenerators(compilation)
1538+
.GetRunResult();
1539+
1540+
var expected = """
1541+
namespace GeneratorTests;
1542+
1543+
public static partial class ServicesExtensions
1544+
{
1545+
public static partial global::System.Collections.Generic.IEnumerable<global::GeneratorTests.ServiceInfo> GetServiceInfos()
1546+
{
1547+
return [
1548+
GetServiceInfo<global::GeneratorTests.MyService1>(),
1549+
GetServiceInfo<global::GeneratorTests.MyService2>()
1550+
];
1551+
}
1552+
}
1553+
""";
1554+
Assert.Equal(expected, results.GeneratedTrees[2].ToString());
1555+
}
1556+
1557+
[Fact]
1558+
public void ScanForTypesAttribute_ReturnsTypeArray_MultipleAttributes()
1559+
{
1560+
var source = """
1561+
using ServiceScan.SourceGenerator;
1562+
using System;
1563+
1564+
namespace GeneratorTests;
1565+
1566+
public static partial class ServicesExtensions
1567+
{
1568+
[ScanForTypes(AssignableTo = typeof(IFirstService))]
1569+
[ScanForTypes(AssignableTo = typeof(ISecondService))]
1570+
public static partial Type[] GetServiceTypes();
1571+
}
1572+
""";
1573+
1574+
var services =
1575+
"""
1576+
namespace GeneratorTests;
1577+
1578+
public interface IFirstService { }
1579+
public interface ISecondService { }
1580+
public class MyService1 : IFirstService { }
1581+
public class MyService2 : ISecondService { }
1582+
""";
1583+
1584+
var compilation = CreateCompilation(source, services);
1585+
1586+
var results = CSharpGeneratorDriver
1587+
.Create(_generator)
1588+
.RunGenerators(compilation)
1589+
.GetRunResult();
1590+
1591+
var expected = """
1592+
namespace GeneratorTests;
1593+
1594+
public static partial class ServicesExtensions
1595+
{
1596+
public static partial global::System.Type[] GetServiceTypes()
1597+
{
1598+
return [
1599+
typeof(global::GeneratorTests.MyService1),
1600+
typeof(global::GeneratorTests.MyService2)
1601+
];
1602+
}
1603+
}
1604+
""";
1605+
Assert.Equal(expected, results.GeneratedTrees[2].ToString());
1606+
}
1607+
1608+
[Fact]
1609+
public void ScanForTypesAttribute_HandlerReturnTypeMismatch_ReportsDiagnostic()
1610+
{
1611+
var source = """
1612+
using ServiceScan.SourceGenerator;
1613+
1614+
namespace GeneratorTests;
1615+
1616+
public static partial class ServicesExtensions
1617+
{
1618+
[ScanForTypes(AssignableTo = typeof(IService), Handler = nameof(GetServiceName))]
1619+
public static partial ServiceInfo[] GetServiceInfos();
1620+
1621+
private static string GetServiceName<T>() => typeof(T).Name;
1622+
}
1623+
""";
1624+
1625+
var services =
1626+
"""
1627+
namespace GeneratorTests;
1628+
1629+
public interface IService { }
1630+
public class MyService : IService { }
1631+
1632+
public class ServiceInfo { }
1633+
""";
1634+
1635+
var compilation = CreateCompilation(source, services);
1636+
1637+
var results = CSharpGeneratorDriver
1638+
.Create(_generator)
1639+
.RunGenerators(compilation)
1640+
.GetRunResult();
1641+
1642+
Assert.Equal(results.Diagnostics.Single().Descriptor, DiagnosticDescriptors.WrongHandlerReturnTypeForCollectionReturn);
1643+
}
1644+
1645+
[Fact]
1646+
public void ScanForTypesAttribute_NoHandlerNonTypeCollection_ReportsDiagnostic()
1647+
{
1648+
var source = """
1649+
using ServiceScan.SourceGenerator;
1650+
1651+
namespace GeneratorTests;
1652+
1653+
public static partial class ServicesExtensions
1654+
{
1655+
[ScanForTypes(AssignableTo = typeof(IService))]
1656+
public static partial string[] GetServiceNames();
1657+
}
1658+
""";
1659+
1660+
var services =
1661+
"""
1662+
namespace GeneratorTests;
1663+
1664+
public interface IService { }
1665+
public class MyService : IService { }
1666+
""";
1667+
1668+
var compilation = CreateCompilation(source, services);
1669+
1670+
var results = CSharpGeneratorDriver
1671+
.Create(_generator)
1672+
.RunGenerators(compilation)
1673+
.GetRunResult();
1674+
1675+
Assert.Equal(results.Diagnostics.Single().Descriptor, DiagnosticDescriptors.MissingCustomHandlerOnGenerateServiceHandler);
1676+
}
13461677
}

0 commit comments

Comments
 (0)