Skip to content

Commit

Permalink
Merge pull request #45 from Serg046/issue-41
Browse files Browse the repository at this point in the history
Issue #41: Ability to register composition roots with fewer API calls
  • Loading branch information
NikolayPianikov authored Mar 24, 2024
2 parents 0d502fb + 6855a9d commit 9adddd6
Show file tree
Hide file tree
Showing 5 changed files with 413 additions and 30 deletions.
37 changes: 31 additions & 6 deletions src/Pure.DI.Core/Components/Api.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1305,7 +1305,7 @@ internal interface IConfiguration
/// <typeparam name="T1">The type 1 of dependency to be bound.</typeparam>
/// <typeparam name="T2">The type 2 of dependency to be bound.</typeparam>
/// <typeparam name="T3">The type 3 of dependency to be bound.</typeparam>
/// <typeparam name="T4">The type 3 of dependency to be bound.</typeparam>
/// <typeparam name="T4">The type 4 of dependency to be bound.</typeparam>
/// <param name="tags">The optional argument that specifies tags for a particular type of dependency binding.</param>
/// <returns>Reference to the setup continuation chain.</returns>
/// <seealso cref="IBinding.As"/>
Expand All @@ -1318,7 +1318,7 @@ internal interface IConfiguration
/// <typeparam name="T1">The type 1 of dependency to be bound.</typeparam>
/// <typeparam name="T2">The type 2 of dependency to be bound.</typeparam>
/// <typeparam name="T3">The type 3 of dependency to be bound.</typeparam>
/// <typeparam name="T4">The type 3 of dependency to be bound.</typeparam>
/// <typeparam name="T4">The type 4 of dependency to be bound.</typeparam>
/// <typeparam name="T5">The type 5 of dependency to be bound.</typeparam>
/// <param name="tags">The optional argument that specifies tags for a particular type of dependency binding.</param>
/// <returns>Reference to the setup continuation chain.</returns>
Expand All @@ -1332,7 +1332,7 @@ internal interface IConfiguration
/// <typeparam name="T1">The type 1 of dependency to be bound.</typeparam>
/// <typeparam name="T2">The type 2 of dependency to be bound.</typeparam>
/// <typeparam name="T3">The type 3 of dependency to be bound.</typeparam>
/// <typeparam name="T4">The type 3 of dependency to be bound.</typeparam>
/// <typeparam name="T4">The type 4 of dependency to be bound.</typeparam>
/// <typeparam name="T5">The type 5 of dependency to be bound.</typeparam>
/// <typeparam name="T6">The type 6 of dependency to be bound.</typeparam>
/// <param name="tags">The optional argument that specifies tags for a particular type of dependency binding.</param>
Expand All @@ -1347,7 +1347,7 @@ internal interface IConfiguration
/// <typeparam name="T1">The type 1 of dependency to be bound.</typeparam>
/// <typeparam name="T2">The type 2 of dependency to be bound.</typeparam>
/// <typeparam name="T3">The type 3 of dependency to be bound.</typeparam>
/// <typeparam name="T4">The type 3 of dependency to be bound.</typeparam>
/// <typeparam name="T4">The type 4 of dependency to be bound.</typeparam>
/// <typeparam name="T5">The type 5 of dependency to be bound.</typeparam>
/// <typeparam name="T6">The type 6 of dependency to be bound.</typeparam>
/// <typeparam name="T7">The type 7 of dependency to be bound.</typeparam>
Expand All @@ -1363,7 +1363,7 @@ internal interface IConfiguration
/// <typeparam name="T1">The type 1 of dependency to be bound.</typeparam>
/// <typeparam name="T2">The type 2 of dependency to be bound.</typeparam>
/// <typeparam name="T3">The type 3 of dependency to be bound.</typeparam>
/// <typeparam name="T4">The type 3 of dependency to be bound.</typeparam>
/// <typeparam name="T4">The type 4 of dependency to be bound.</typeparam>
/// <typeparam name="T5">The type 5 of dependency to be bound.</typeparam>
/// <typeparam name="T6">The type 6 of dependency to be bound.</typeparam>
/// <typeparam name="T7">The type 7 of dependency to be bound.</typeparam>
Expand All @@ -1373,7 +1373,25 @@ internal interface IConfiguration
/// <seealso cref="IBinding.As"/>
/// <seealso cref="IBinding.To{T}()"/>
IBinding Bind<T1, T2, T3, T4, T5, T6, T7, T8>(params object[] tags);


/// <summary>
/// Begins the definition of the binding with <see cref="Root{T}"/> applied.
/// </summary>
/// <example>
/// <code>
/// DI.Setup("Composition")
/// .RootBind&lt;IService&gt;();
/// </code>
/// </example>
/// <typeparam name="T">The type of dependency to be bound.</typeparam>
/// <param name="name">Specifies the unique name of the root of the composition. If the value is empty, a private root will be created, which can be used when calling <c>Resolve</c> methods.</param>
/// <param name="kind">The Optional argument specifying the kind for the root of the Composition.</param>
/// <param name="tags">The optional argument that specifies tags for a particular type of dependency binding. If is is not empty, the first tag is used for the root.</param>
/// <returns>Reference to the setup continuation chain.</returns>
/// <seealso cref="IBinding.As"/>
/// <seealso cref="IBinding.To{T}()"/>
IBinding RootBind<T>(string name = "", RootKinds kind = RootKinds.Default, params object[] tags);

/// <summary>
/// Indicates the use of some single or multiple setups as base setups by name.
/// </summary>
Expand Down Expand Up @@ -2018,6 +2036,13 @@ public IBinding Bind<T1, T2, T3, T4, T5, T6, T7, T8>(params object[] tags)
return Binding.Shared;
}

/// <inheritdoc />
[global::System.Runtime.CompilerServices.MethodImpl((global::System.Runtime.CompilerServices.MethodImplOptions)256)]
public IBinding RootBind<T>(string name = "", RootKinds kind = RootKinds.Default, params object[] tags)
{
return Binding.Shared;
}

/// <inheritdoc />
[global::System.Runtime.CompilerServices.MethodImpl((global::System.Runtime.CompilerServices.MethodImplOptions)256)]
public IConfiguration DependsOn(params string[] setupNames)
Expand Down
80 changes: 56 additions & 24 deletions src/Pure.DI.Core/Core/ApiInvocationProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,21 +149,16 @@ public void ProcessInvocation(
switch (genericName.Identifier.Text)
{
case nameof(IConfiguration.Bind):
if (genericName.TypeArgumentList.Arguments is var contractTypes)
{
foreach (var contractType in contractTypes)
{
metadataVisitor.VisitContract(
new MdContract(
semanticModel,
invocation.ArgumentList,
semanticModel.GetTypeSymbol<ITypeSymbol>(contractType, cancellationToken),
BuildTags(semanticModel, invocation.ArgumentList.Arguments).ToImmutable()));
}
}
VisitBind(metadataVisitor, semanticModel, invocation, genericName, cancellationToken);
break;

case nameof(IConfiguration.RootBind):
var tags = invocation.ArgumentList.Arguments.SkipWhile((arg, i) => arg.NameColon?.Name.Identifier.Text != "tags" && i < 2).ToList();
VisitBind(metadataVisitor, semanticModel, invocation, tags.ToList(), genericName, cancellationToken);
var rootTag = tags.Select(t => semanticModel.GetConstantValue<object>(t.Expression)).FirstOrDefault();
VisitRoot(arguments, rootTag, metadataVisitor, semanticModel, invocation, invocationComments, genericName, cancellationToken);
break;

case nameof(IBinding.To):
if (genericName.TypeArgumentList.Arguments is [{ } implementationTypeSyntax])
{
Expand Down Expand Up @@ -201,16 +196,7 @@ public void ProcessInvocation(
break;

case nameof(IConfiguration.Root):
if (genericName.TypeArgumentList.Arguments is [{ } rootType])
{
var rootSymbol = semanticModel.GetTypeSymbol<INamedTypeSymbol>(rootType, cancellationToken);
var rootArgs = arguments.GetArgs(invocation.ArgumentList, "name", "tag", "kind");
var name = rootArgs[0] is {} nameArg ? semanticModel.GetConstantValue<string>(nameArg.Expression) ?? "" : "";
var tag = rootArgs[1] is {} tagArg ? semanticModel.GetConstantValue<object>(tagArg.Expression) : null;
var kind = rootArgs[2] is {} kindArg ? semanticModel.GetConstantValue<RootKinds>(kindArg.Expression) : RootKinds.Default;
metadataVisitor.VisitRoot(new MdRoot(rootType, semanticModel, rootSymbol, name, new MdTag(0, tag), kind, invocationComments));
}

VisitRoot(arguments, metadataVisitor, semanticModel, invocation, invocationComments, genericName, cancellationToken);
break;

case nameof(IConfiguration.TypeAttribute):
Expand Down Expand Up @@ -266,6 +252,52 @@ public void ProcessInvocation(
}
}

private static void VisitRoot(IArguments arguments, IMetadataVisitor metadataVisitor, SemanticModel semanticModel, InvocationExpressionSyntax invocation, List<string> invocationComments, GenericNameSyntax genericName, CancellationToken cancellationToken)
{
if (genericName.TypeArgumentList.Arguments is [{ } rootType])
{
var rootSymbol = semanticModel.GetTypeSymbol<INamedTypeSymbol>(rootType, cancellationToken);
var rootArgs = arguments.GetArgs(invocation.ArgumentList, "name", "tag", "kind");
var name = rootArgs[0] is { } nameArg ? semanticModel.GetConstantValue<string>(nameArg.Expression) ?? "" : "";
var tag = rootArgs[1] is { } tagArg ? semanticModel.GetConstantValue<object>(tagArg.Expression) : null;
var kind = rootArgs[2] is { } kindArg ? semanticModel.GetConstantValue<RootKinds>(kindArg.Expression) : RootKinds.Default;
metadataVisitor.VisitRoot(new MdRoot(rootType, semanticModel, rootSymbol, name, new MdTag(0, tag), kind, invocationComments));
}
}

private static void VisitRoot(IArguments arguments, object? tag, IMetadataVisitor metadataVisitor, SemanticModel semanticModel, InvocationExpressionSyntax invocation, List<string> invocationComments, GenericNameSyntax genericName, CancellationToken cancellationToken)
{
if (genericName.TypeArgumentList.Arguments is [{ } rootType])
{
var rootSymbol = semanticModel.GetTypeSymbol<INamedTypeSymbol>(rootType, cancellationToken);
var rootArgs = arguments.GetArgs(invocation.ArgumentList, "name", "kind");
var name = rootArgs[0] is { } nameArg ? semanticModel.GetConstantValue<string>(nameArg.Expression) ?? "" : "";
var kind = rootArgs[1] is { } kindArg ? semanticModel.GetConstantValue<RootKinds>(kindArg.Expression) : RootKinds.Default;
metadataVisitor.VisitRoot(new MdRoot(rootType, semanticModel, rootSymbol, name, new MdTag(0, tag), kind, invocationComments));
}
}

private static void VisitBind(IMetadataVisitor metadataVisitor, SemanticModel semanticModel, InvocationExpressionSyntax invocation, GenericNameSyntax genericName, CancellationToken cancellationToken)
{
VisitBind(metadataVisitor, semanticModel, invocation, invocation.ArgumentList.Arguments, genericName, cancellationToken);
}

private static void VisitBind(IMetadataVisitor metadataVisitor, SemanticModel semanticModel, InvocationExpressionSyntax invocation, IReadOnlyList<ArgumentSyntax> bindTags, GenericNameSyntax genericName, CancellationToken cancellationToken)
{
if (genericName.TypeArgumentList.Arguments is var contractTypes)
{
foreach (var contractType in contractTypes)
{
metadataVisitor.VisitContract(
new MdContract(
semanticModel,
invocation.ArgumentList,
semanticModel.GetTypeSymbol<ITypeSymbol>(contractType, cancellationToken),
BuildTags(semanticModel, bindTags).ToImmutable()));
}
}
}

private void VisitArg(
IMetadataVisitor metadataVisitor,
SemanticModel semanticModel,
Expand Down Expand Up @@ -438,7 +470,7 @@ private static List<T> BuildConstantArgs<T>(SemanticModel semanticModel, Separat
return values;
}

private static ImmutableArray<MdTag>.Builder BuildTags(SemanticModel semanticModel, SeparatedSyntaxList<ArgumentSyntax> arguments)
private static ImmutableArray<MdTag>.Builder BuildTags(SemanticModel semanticModel, IReadOnlyList<ArgumentSyntax> arguments)
{
var builder = ImmutableArray.CreateBuilder<MdTag>();
for (var index = 0; index < arguments.Count; index++)
Expand Down
Loading

0 comments on commit 9adddd6

Please sign in to comment.