Skip to content

Commit

Permalink
Generic roots for collections
Browse files Browse the repository at this point in the history
  • Loading branch information
NikolayPianikov committed Apr 17, 2024
1 parent 8bcb663 commit 5fb52f5
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 14 deletions.
10 changes: 5 additions & 5 deletions src/Pure.DI.Core/Core/Code/ConstructCodeBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,10 @@ private void BuildEnumerable(BuildContext ctx, in DpConstruct enumerable, string
ctx.Code.AppendLines(ctx.BuildTools.OnCreated(ctx, variable));
}

private static void BuildArray(BuildContext ctx, in DpConstruct array)
private void BuildArray(BuildContext ctx, in DpConstruct array)
{
var variable = ctx.Variable;
var instantiation = $"new {array.Source.ElementType}[{variable.Args.Count.ToString()}] {{ {string.Join(", ", variable.Args.Select(i => ctx.BuildTools.OnInjected(ctx, i.Current)))} }}";
var instantiation = $"new {typeResolver.Resolve(array.Source.ElementType)}[{variable.Args.Count.ToString()}] {{ {string.Join(", ", variable.Args.Select(i => ctx.BuildTools.OnInjected(ctx, i.Current)))} }}";
var onCreated = ctx.BuildTools.OnCreated(ctx, variable).ToArray();
if (onCreated.Any())
{
Expand All @@ -96,16 +96,16 @@ private static void BuildArray(BuildContext ctx, in DpConstruct array)
}
}

private static void BuildSpan(BuildContext ctx, in DpConstruct span)
private void BuildSpan(BuildContext ctx, in DpConstruct span)
{
var variable = ctx.Variable;
var createArray = $"{span.Source.ElementType}[{variable.Args.Count.ToString()}] {{ {string.Join(", ", variable.Args.Select(i => ctx.BuildTools.OnInjected(ctx, i.Current)))} }}";
var createArray = $"{typeResolver.Resolve(span.Source.ElementType)}[{variable.Args.Count.ToString()}] {{ {string.Join(", ", variable.Args.Select(i => ctx.BuildTools.OnInjected(ctx, i.Current)))} }}";

var isStackalloc =
span.Source.ElementType.IsValueType
&& span.Binding.SemanticModel.Compilation.GetLanguageVersion() >= LanguageVersion.CSharp7_3;

var createInstance = isStackalloc ? $"stackalloc {createArray}" : $"new {Names.SystemNamespace}Span<{span.Source.ElementType}>(new {createArray})";
var createInstance = isStackalloc ? $"stackalloc {createArray}" : $"new {Names.SystemNamespace}Span<{typeResolver.Resolve(span.Source.ElementType)}>(new {createArray})";
ctx.Code.AppendLine($"{ctx.BuildTools.GetDeclaration(variable)}{variable.VariableName} = {createInstance};");
ctx.Code.AppendLines(ctx.BuildTools.OnCreated(ctx, variable));
}
Expand Down
2 changes: 1 addition & 1 deletion src/Pure.DI.Core/Core/DependencyGraphBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public IEnumerable<DependencyNode> TryBuild(
cancellationToken.ThrowIfCancellationRequested();
if (map.TryGetValue(injection, out var sourceNode))
{
if (!marker.IsMarkerBased(sourceNode.Type))
if (sourceNode.Construct is not null || !marker.IsMarkerBased(sourceNode.Type))
{
continue;
}
Expand Down
6 changes: 6 additions & 0 deletions src/Pure.DI.Core/Core/VariationalDependencyGraphBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,14 @@ internal sealed class VariationalDependencyGraphBuilder(
{
var maxIterations = globalOptions.MaxIterations;
DependencyGraph? first = default;
var maxAttempts = 0x2000;
while (variator.TryGetNextVariants(variants, node => !node.HasNode, out var nodes))
{
if (maxAttempts-- == 0)
{
throw new CompileErrorException("It is not possible to construct a dependency graph.", setup.Source.GetLocation(), LogId.ErrorInvalidMetadata);
}

if (maxIterations-- <= 0)
{
logger.CompileError($"The maximum number of iterations {globalOptions.MaxIterations.ToString()} was exceeded when building the optimal dependency graph. Try to specify the dependency graph more accurately.", setup.Source.GetLocation(), LogId.ErrorInvalidMetadata);
Expand Down
174 changes: 166 additions & 8 deletions tests/Pure.DI.IntegrationTests/GenericRootsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public static void Main()

// Then
result.Success.ShouldBeTrue(result);
result.StdOut.ShouldBe(ImmutableArray.Create("Sample.CardboardBox`1[System.Int32]"), result);
result.StdOut.ShouldBe(["Sample.CardboardBox`1[System.Int32]"], result);
}

[Fact]
Expand Down Expand Up @@ -113,7 +113,7 @@ public static void Main()
result.Errors.Count.ShouldBe(0);
result.Warnings.Count.ShouldBe(1);
result.Warnings.Count(i => i.Id == LogId.WarningTypeArgInResolveMethod).ShouldBe(1);
result.StdOut.ShouldBe(ImmutableArray.Create("Sample.CardboardBox`1[System.Int32]"), result);
result.StdOut.ShouldBe(["Sample.CardboardBox`1[System.Int32]"], result);
}

[Fact]
Expand Down Expand Up @@ -180,9 +180,10 @@ public static void Main()
// Then
result.Success.ShouldBeTrue(result);
result.StdOut.ShouldBe(
ImmutableArray.Create(
"Sample.Service`2[System.Int32,System.DateTime]",
"Sample.OtherService`1[System.String]"), result);
[
"Sample.Service`2[System.Int32,System.DateTime]",
"Sample.OtherService`1[System.String]"
], result);
}

[Fact]
Expand Down Expand Up @@ -242,7 +243,7 @@ public static void Main()

// Then
result.Success.ShouldBeTrue(result);
result.StdOut.ShouldBe(ImmutableArray.Create("Sample.Consumer`1[System.Int32]"), result);
result.StdOut.ShouldBe(["Sample.Consumer`1[System.Int32]"], result);
}

[Fact]
Expand Down Expand Up @@ -314,7 +315,7 @@ public static void Main()

// Then
result.Success.ShouldBeTrue(result);
result.StdOut.ShouldBe(ImmutableArray.Create("Sample.CardboardBox`2[Sample.Disposable,System.Int32]"), result);
result.StdOut.ShouldBe(["Sample.CardboardBox`2[Sample.Disposable,System.Int32]"], result);
}

[Fact]
Expand Down Expand Up @@ -387,6 +388,163 @@ public static void Main()

// Then
result.Success.ShouldBeTrue(result);
result.StdOut.ShouldBe(ImmutableArray.Create("Sample.Service`2[System.Int32,System.Double]"), result);
result.StdOut.ShouldBe(["Sample.Service`2[System.Int32,System.Double]"], result);
}

[Fact]
public async Task ShouldSupportGenericRootWhenArray()
{
// Given

// When
var result = await """
using System;
using Pure.DI;
using System.Collections.Generic;
using static Pure.DI.Lifetime;
namespace Sample
{
interface IRecipient<T>
{
}
interface IPost<T>
{
}
class Post<T> : IPost<T>
{
public Post(IRecipient<T>[] recipients)
{
}
}
class Recipient<TT> : IRecipient<TT>
{
}
internal partial class Composition
{
void Setup()
{
DI.Setup(nameof(Composition))
.Hint(Hint.Resolve, "Off")
.Bind<IRecipient<TT>>(1).To<Recipient<TT>>()
.Bind<IRecipient<TT>>(2).To<Recipient<TT>>()
.RootBind<IPost<TT>>("GetRoot").To<Post<TT>>();
}
}
public class Program
{
public static void Main()
{
var composition = new Composition();
var root = composition.GetRoot<string>();
Console.WriteLine(root);
}
}
}
""".RunAsync(new Options(LanguageVersion.CSharp9));

// Then
result.Success.ShouldBeTrue(result);
result.StdOut.ShouldBe(["Sample.Post`1[System.String]"], result);
}

[Theory]
[InlineData("System.Collections.Generic.IEnumerable")]
[InlineData("System.Collections.Generic.IList")]
[InlineData("System.Collections.Immutable.ImmutableArray")]
[InlineData("System.Collections.Generic.IReadOnlyList")]
[InlineData("System.Collections.Generic.ISet")]
[InlineData("System.Collections.Generic.HashSet")]
[InlineData("System.Collections.Generic.SortedSet")]
[InlineData("System.Collections.Generic.Queue")]
[InlineData("System.Collections.Generic.Stack")]
[InlineData("System.Collections.Immutable.IImmutableList")]
[InlineData("System.Collections.Immutable.ImmutableList")]
[InlineData("System.Collections.Immutable.IImmutableSet")]
[InlineData("System.Collections.Immutable.ImmutableHashSet")]
[InlineData("System.Collections.Immutable.ImmutableSortedSet")]
[InlineData("System.Collections.Immutable.IImmutableQueue")]
[InlineData("System.Collections.Immutable.ImmutableQueue")]
[InlineData("System.Collections.Immutable.IImmutableStack")]
[InlineData("System.Collections.Immutable.ImmutableStack")]
[InlineData("System.Span")]
[InlineData("System.Span", LanguageVersion.CSharp8)]
[InlineData("System.ReadOnlySpan")]
[InlineData("System.Memory")]
[InlineData("System.ReadOnlyMemory")]
[InlineData("System.Collections.Concurrent.IProducerConsumerCollection")]
[InlineData("System.Collections.Concurrent.ConcurrentBag")]
[InlineData("System.Collections.Concurrent.ConcurrentQueue")]
[InlineData("System.Collections.Concurrent.ConcurrentStack")]
[InlineData("System.Collections.Concurrent.BlockingCollection")]
public async Task ShouldSupportGenericRootWhenCollection(string collectionType, LanguageVersion languageVersion = LanguageVersion.CSharp9)
{
// Given

// When
var result = await """
using System;
using Pure.DI;
using System.Collections.Generic;
using static Pure.DI.Lifetime;
namespace Sample
{
interface IRecipient<T>
{
}
interface IPost<T>
{
}
class Post<T> : IPost<T>
{
public Post(###CollectionType###<IRecipient<T>> recipients)
{
}
}
class Recipient<TT> : IRecipient<TT>
{
}
internal partial class Composition
{
void Setup()
{
DI.Setup(nameof(Composition))
.Hint(Hint.Resolve, "Off")
.Bind<IRecipient<TT>>(1).To<Recipient<TT>>()
.Bind<IRecipient<TT>>(2).To<Recipient<TT>>()
.RootBind<IPost<TT>>("GetRoot").To<Post<TT>>();
}
}
public class Program
{
public static void Main()
{
var composition = new Composition();
var root = composition.GetRoot<string>();
Console.WriteLine(root);
}
}
}
""".Replace("###CollectionType###", collectionType).RunAsync(
new Options
{
LanguageVersion = languageVersion,
NullableContextOptions = NullableContextOptions.Disable
});

// Then
result.Success.ShouldBeTrue(result);
result.StdOut.ShouldBe(["Sample.Post`1[System.String]"], result);
}
}

0 comments on commit 5fb52f5

Please sign in to comment.