diff --git a/src/Pure.DI.Core/Core/Code/CompositionBuilder.cs b/src/Pure.DI.Core/Core/Code/CompositionBuilder.cs index 7f5f956ea..d525a9e65 100644 --- a/src/Pure.DI.Core/Core/Code/CompositionBuilder.cs +++ b/src/Pure.DI.Core/Core/Code/CompositionBuilder.cs @@ -27,18 +27,6 @@ public CompositionCode Build(DependencyGraph graph) break; } - if (root.Source.Kind.HasFlag(RootKinds.Light)) - { - var lightweightRoot = root with - { - Lines = new Lines(), - TypeDescription = typeResolver.Resolve(graph.Source, root.Injection.Type) - }; - - roots.Add(lightweightRoot); - continue; - } - var lines = new Lines(); using var rootToken = varsMap.Root(lines); var ctx = new RootContext(graph, root, varsMap, lines); @@ -96,6 +84,11 @@ public CompositionCode Build(DependencyGraph graph) IsMethod = isMethod }; + if (processedRoot.Kind.HasFlag(RootKinds.Light) && typeDescription.TypeArgs.Count > 0) + { + processedRoot = processedRoot with { Kind = processedRoot.Kind & ~RootKinds.Light }; + } + roots.Add(processedRoot); } diff --git a/src/Pure.DI.Core/Core/Code/Parts/LightweightRootClassBuilder.cs b/src/Pure.DI.Core/Core/Code/Parts/LightweightRootClassBuilder.cs index 84aa736c2..e09da0119 100644 --- a/src/Pure.DI.Core/Core/Code/Parts/LightweightRootClassBuilder.cs +++ b/src/Pure.DI.Core/Core/Code/Parts/LightweightRootClassBuilder.cs @@ -8,7 +8,9 @@ class LightweightRootClassBuilder( public CompositionCode Build(CompositionCode composition) { - var roots = composition.PublicRoots.Where(i => i.Kind.HasFlag(RootKinds.Light)).ToList(); + var roots = composition.PublicRoots + .Where(i => i.Kind.HasFlag(RootKinds.Light)) + .ToList(); if (roots.Count == 0) { return composition; @@ -32,4 +34,4 @@ public CompositionCode Build(CompositionCode composition) return composition with { MembersCount = membersCount }; } -} \ No newline at end of file +} diff --git a/src/Pure.DI.Core/Core/Code/Parts/RootMethodsBuilder.cs b/src/Pure.DI.Core/Core/Code/Parts/RootMethodsBuilder.cs index 8fc59fe36..95afdffd0 100644 --- a/src/Pure.DI.Core/Core/Code/Parts/RootMethodsBuilder.cs +++ b/src/Pure.DI.Core/Core/Code/Parts/RootMethodsBuilder.cs @@ -163,10 +163,19 @@ private void BuildRoot(CompositionCode composition, Root root) else { Lines lines; - if (root.Source.Kind.HasFlag(RootKinds.Light)) + if (root.Kind.HasFlag(RootKinds.Light)) { lines = new Lines(); - lines.AppendLine($"return {Names.LightweightRootName}.{root.Source.UniqueName}();"); + var compositionTypeName = composition.Source.Source.Name.ClassName; + var compositionInstance = root.IsStatic ? $"new {compositionTypeName}()." : string.Empty; + if (root.RootArgs.IsEmpty) + { + lines.AppendLine($"return {compositionInstance}{Names.LightweightRootName}.{root.Source.UniqueName}();"); + } + else + { + lines.AppendLine($"return {compositionInstance}{Names.LightweightRootName}({string.Join(", ", root.RootArgs.Select(i => i.Name))}).{root.Source.UniqueName}();"); + } } else { @@ -204,4 +213,4 @@ private void BuildRoot(CompositionCode composition, Root root) code.AppendLine("#pragma warning restore CS0162"); } } -} \ No newline at end of file +} diff --git a/src/Pure.DI.Core/Core/DependencyGraphBuilder.cs b/src/Pure.DI.Core/Core/DependencyGraphBuilder.cs index 0b3c116f8..ae3ab1fff 100644 --- a/src/Pure.DI.Core/Core/DependencyGraphBuilder.cs +++ b/src/Pure.DI.Core/Core/DependencyGraphBuilder.cs @@ -57,7 +57,8 @@ public IEnumerable Build(GraphBuildContext ctx) } else { - if (node.Root.Source.Kind.HasFlag(RootKinds.Light)) + if (node.Root.Source.Kind.HasFlag(RootKinds.Light) + && node.Root.Source.RootType is not INamedTypeSymbol { IsGenericType: true }) { processed.Add(processingNode); } @@ -562,4 +563,4 @@ private IProcessingNode CreateNewProcessingNode(ICache node.Factory is { Source.HasContextTag: true } ? injection.Tag : null; -} \ No newline at end of file +} diff --git a/src/Pure.DI.Core/Core/RootDependencyNodeBuilder.cs b/src/Pure.DI.Core/Core/RootDependencyNodeBuilder.cs index deb96d8d7..f114c8e5a 100644 --- a/src/Pure.DI.Core/Core/RootDependencyNodeBuilder.cs +++ b/src/Pure.DI.Core/Core/RootDependencyNodeBuilder.cs @@ -41,7 +41,8 @@ public IEnumerable Build(DependencyNodeBuildContext ctx) // ReSharper disable once InvertIf if (setup.Kind == CompositionKind.Public - && roots.Any(i => i.Kind.HasFlag(RootKinds.Light)) + && roots.Any(i => i.Kind.HasFlag(RootKinds.Light) + && i.RootType is not INamedTypeSymbol { IsGenericType: true }) && types.TryGet(SpecialType.LightweightRoot, setup.SemanticModel.Compilation) is {} rootType) { var root = new MdRoot( @@ -79,4 +80,4 @@ public IEnumerable Build(DependencyNodeBuildContext ctx) locationProvider)); } } -} \ No newline at end of file +} diff --git a/tests/Pure.DI.IntegrationTests/LightweightRootsTests.cs b/tests/Pure.DI.IntegrationTests/LightweightRootsTests.cs index 528e49cf7..b33f882da 100644 --- a/tests/Pure.DI.IntegrationTests/LightweightRootsTests.cs +++ b/tests/Pure.DI.IntegrationTests/LightweightRootsTests.cs @@ -15,28 +15,28 @@ public async Task ShouldSupportLightweightRoot() namespace Sample { interface IDependency {} - + class Dependency: IDependency { } - + interface IService { IDependency? Dep { get; } } - - class Service: IService + + class Service: IService { [Ordinal(1)] internal void Initialize([Tag(374)] string depName) { Console.WriteLine($"Initialize 1 {depName}"); } - + [Ordinal(0)] public IDependency? Dep { get; set; } } - + static class Setup { private static void SetupComposition() @@ -47,7 +47,7 @@ private static void SetupComposition() .Root("Root", kind: RootKinds.Light); } } - + public class Program { public static void Main() @@ -58,7 +58,7 @@ public static void Main() } } } - """.RunAsync(); + """.RunAsync(new Options(LanguageVersion.Preview)); // Then result.Success.ShouldBeTrue(result); @@ -78,28 +78,28 @@ public async Task ShouldSupportLightweightRootWhenNoName() namespace Sample { interface IDependency {} - + class Dependency: IDependency { } - + interface IService { IDependency? Dep { get; } } - - class Service: IService + + class Service: IService { [Ordinal(1)] internal void Initialize([Tag(374)] string depName) { Console.WriteLine($"Initialize 1 {depName}"); } - + [Ordinal(0)] public IDependency? Dep { get; set; } } - + static class Setup { private static void SetupComposition() @@ -110,7 +110,7 @@ private static void SetupComposition() .Root(kind: RootKinds.Light); } } - + public class Program { public static void Main() @@ -120,7 +120,7 @@ public static void Main() } } } - """.RunAsync(); + """.RunAsync(new Options(LanguageVersion.Preview)); // Then result.Success.ShouldBeTrue(result); @@ -140,28 +140,28 @@ public async Task ShouldSupportSeveralLightweightRoots() namespace Sample { interface IDependency {} - + class Dependency: IDependency { } - + interface IService { IDependency? Dep { get; } } - - class Service: IService + + class Service: IService { [Ordinal(1)] internal void Initialize([Tag(374)] string depName) { Console.WriteLine($"Initialize 1 {depName}"); } - + [Ordinal(0)] public IDependency? Dep { get; set; } } - + class Xyz { public Xyz() @@ -169,7 +169,7 @@ public Xyz() Console.WriteLine("Xyz"); } } - + static class Setup { private static void SetupComposition() @@ -181,7 +181,7 @@ private static void SetupComposition() .Root("Xyz", kind: RootKinds.Light); } } - + public class Program { public static void Main() @@ -193,10 +193,1271 @@ public static void Main() } } } - """.RunAsync(); + """.RunAsync(new Options(LanguageVersion.Preview)); // Then result.Success.ShouldBeTrue(result); result.StdOut.ShouldBe(["Initialize 1 Abc", "Initialize 1 Abc", "Xyz"], result); } -} \ No newline at end of file + + [Fact] + public async Task ShouldSupportLightweightRootWithStringArgument() + { + // Given + + // When + var result = await """ + using System; + using Pure.DI; + + namespace Sample; + + interface IService { string Name { get; } } + class Service(string name) : IService { public string Name { get; } = name; } + + partial class Composition + { + void Setup() => DI.Setup(nameof(Composition)) + .Hint(Hint.Resolve, "Off") + .RootArg("name") + .Bind().To() + .Root("GetService", kind: RootKinds.Light); + } + + class Program + { + static void Main() + { + var composition = new Composition(); + Console.WriteLine(composition.GetService("abc").Name); + } + } + """.RunAsync(new Options(LanguageVersion.Preview)); + + // Then + result.Success.ShouldBeTrue(result); + result.StdOut.ShouldBe(["abc"], result); + } + + [Fact] + public async Task ShouldSupportLightweightRootWithIntArgument() + { + // Given + + // When + var result = await """ + using System; + using Pure.DI; + + namespace Sample; + + interface IService { int Id { get; } } + class Service(int id) : IService { public int Id { get; } = id; } + + partial class Composition + { + void Setup() => DI.Setup(nameof(Composition)) + .Hint(Hint.Resolve, "Off") + .RootArg("id") + .Bind().To() + .Root("GetService", kind: RootKinds.Light); + } + + class Program + { + static void Main() + { + var composition = new Composition(); + Console.WriteLine(composition.GetService(42).Id); + } + } + """.RunAsync(new Options(LanguageVersion.Preview)); + + // Then + result.Success.ShouldBeTrue(result); + result.StdOut.ShouldBe(["42"], result); + } + + [Fact] + public async Task ShouldSupportLightweightRootWithCustomClassArgument() + { + // Given + + // When + var result = await """ + using System; + using Pure.DI; + + namespace Sample; + + class User(string name) { public string Name { get; } = name; } + interface IService { string Name { get; } } + class Service(User user) : IService { public string Name { get; } = user.Name; } + + partial class Composition + { + void Setup() => DI.Setup(nameof(Composition)) + .Hint(Hint.Resolve, "Off") + .RootArg("user") + .Bind().To() + .Root("GetService", kind: RootKinds.Light); + } + + class Program + { + static void Main() + { + var composition = new Composition(); + Console.WriteLine(composition.GetService(new User("Bob")).Name); + } + } + """.RunAsync(new Options(LanguageVersion.Preview)); + + // Then + result.Success.ShouldBeTrue(result); + result.StdOut.ShouldBe(["Bob"], result); + } + + [Fact] + public async Task ShouldSupportLightweightRootWithTwoArguments() + { + // Given + + // When + var result = await """ + using System; + using Pure.DI; + + namespace Sample; + + interface IService { string Value { get; } } + class Service(int id, string name) : IService { public string Value { get; } = $"{id}:{name}"; } + + partial class Composition + { + void Setup() => DI.Setup(nameof(Composition)) + .Hint(Hint.Resolve, "Off") + .RootArg("id") + .RootArg("name") + .Bind().To() + .Root("Create", kind: RootKinds.Light); + } + + class Program + { + static void Main() + { + var composition = new Composition(); + Console.WriteLine(composition.Create(7, "Neo").Value); + } + } + """.RunAsync(new Options(LanguageVersion.Preview)); + + // Then + result.Success.ShouldBeTrue(result); + result.StdOut.ShouldBe(["7:Neo"], result); + } + + [Fact] + public async Task ShouldSupportLightweightRootWithNamedArguments() + { + // Given + + // When + var result = await """ + using System; + using Pure.DI; + + namespace Sample; + + interface IService { string Value { get; } } + class Service(int id, string name) : IService { public string Value { get; } = $"{id}:{name}"; } + + partial class Composition + { + void Setup() => DI.Setup(nameof(Composition)) + .Hint(Hint.Resolve, "Off") + .RootArg("id") + .RootArg("name") + .Bind().To() + .Root("Create", kind: RootKinds.Light); + } + + class Program + { + static void Main() + { + var composition = new Composition(); + Console.WriteLine(composition.Create(name: "Trinity", id: 9).Value); + } + } + """.RunAsync(new Options(LanguageVersion.Preview)); + + // Then + result.Success.ShouldBeTrue(result); + result.StdOut.ShouldBe(["9:Trinity"], result); + } + + [Fact] + public async Task ShouldSupportLightweightRootWithTaggedArgument() + { + // Given + + // When + var result = await """ + using System; + using Pure.DI; + + namespace Sample; + + interface IService { string Token { get; } } + class Service([Tag("token")] string token) : IService { public string Token { get; } = token; } + + partial class Composition + { + void Setup() => DI.Setup(nameof(Composition)) + .Hint(Hint.Resolve, "Off") + .RootArg("value", "token") + .Bind().To() + .Root("Create", kind: RootKinds.Light); + } + + class Program + { + static void Main() + { + var composition = new Composition(); + Console.WriteLine(composition.Create("t-1").Token); + } + } + """.RunAsync(new Options(LanguageVersion.Preview)); + + // Then + result.Success.ShouldBeTrue(result); + result.StdOut.ShouldBe(["t-1"], result); + } + + [Fact] + public async Task ShouldSupportLightweightRootWithSeveralTaggedArguments() + { + // Given + + // When + var result = await """ + using System; + using Pure.DI; + + namespace Sample; + + interface IService { string Value { get; } } + class Service([Tag("a")] int id, [Tag("b")] string name) : IService + { + public string Value { get; } = $"{id}:{name}"; + } + + partial class Composition + { + void Setup() => DI.Setup(nameof(Composition)) + .Hint(Hint.Resolve, "Off") + .RootArg("id", "a") + .RootArg("name", "b") + .Bind().To() + .Root("Create", kind: RootKinds.Light); + } + + class Program + { + static void Main() + { + var composition = new Composition(); + Console.WriteLine(composition.Create(1, "A").Value); + } + } + """.RunAsync(new Options(LanguageVersion.Preview)); + + // Then + result.Success.ShouldBeTrue(result); + result.StdOut.ShouldBe(["1:A"], result); + } + + [Fact] + public async Task ShouldSupportGenericLightweightRootWithSingleTypeParameter() + { + // Given + + // When + var result = await """ + using System; + using Pure.DI; + + namespace Sample; + + interface IService { string Name { get; } } + class Service : IService { public string Name { get; } = typeof(T).Name; } + + partial class Composition + { + void Setup() => DI.Setup(nameof(Composition)) + .Hint(Hint.Resolve, "Off") + .Bind>().To>() + .Root>("GetService", kind: RootKinds.Light); + } + + class Program + { + static void Main() + { + var composition = new Composition(); + Console.WriteLine(composition.GetService().Name); + } + } + """.RunAsync(new Options(LanguageVersion.Preview)); + + // Then + result.Success.ShouldBeTrue(result); + result.StdOut.ShouldBe(["Int32"], result); + } + + [Fact] + public async Task ShouldSupportGenericLightweightRootWithTwoTypeParameters() + { + // Given + + // When + var result = await """ + using System; + using Pure.DI; + + namespace Sample; + + interface IService { string Name { get; } } + class Service : IService + { + public string Name { get; } = $"{typeof(T1).Name}:{typeof(T2).Name}"; + } + + partial class Composition + { + void Setup() => DI.Setup(nameof(Composition)) + .Hint(Hint.Resolve, "Off") + .Bind>().To>() + .Root>("GetService", kind: RootKinds.Light); + } + + class Program + { + static void Main() + { + var composition = new Composition(); + Console.WriteLine(composition.GetService().Name); + } + } + """.RunAsync(new Options(LanguageVersion.Preview)); + + // Then + result.Success.ShouldBeTrue(result); + result.StdOut.ShouldBe(["Int32:String"], result); + } + + [Fact] + public async Task ShouldSupportGenericLightweightRootWithNestedGeneric() + { + // Given + + // When + var result = await """ + using System; + using System.Collections.Generic; + using System.Linq; + using Pure.DI; + + namespace Sample; + + interface IService { int Count { get; } } + class Service(IEnumerable values) : IService + { + public int Count { get; } = values.Count(); + } + + partial class Composition + { + void Setup() => DI.Setup(nameof(Composition)) + .Hint(Hint.Resolve, "Off") + .Bind>().To(_ => new[] { default(TT), default(TT) }) + .Bind>().To>() + .Root>("GetService", kind: RootKinds.Light); + } + + class Program + { + static void Main() + { + var composition = new Composition(); + Console.WriteLine(composition.GetService().Count); + } + } + """.RunAsync(new Options(LanguageVersion.Preview)); + + // Then + result.Success.ShouldBeTrue(result); + result.StdOut.ShouldBe(["2"], result); + } + + [Fact] + public async Task ShouldSupportGenericLightweightRootWithClassConstraint() + { + // Given + + // When + var result = await """ + using System; + using Pure.DI; + + namespace Sample; + + interface IService { string Name { get; } } + class Service : IService { public string Name { get; } = typeof(T).Name; } + class RefType { } + + partial class Composition + { + void Setup() => DI.Setup(nameof(Composition)) + .Hint(Hint.Resolve, "Off") + .Bind>().To>() + .Root>("GetService", kind: RootKinds.Light); + } + + class Program + { + static void Main() + { + var composition = new Composition(); + Console.WriteLine(composition.GetService().Name); + } + } + """.RunAsync(new Options(LanguageVersion.Preview)); + + // Then + result.Success.ShouldBeTrue(result); + result.StdOut.ShouldBe(["RefType"], result); + } + + [Fact] + public async Task ShouldSupportGenericLightweightRootWithStructConstraint() + { + // Given + + // When + var result = await """ + using System; + using Pure.DI; + + namespace Sample; + + interface IService { string Name { get; } } + class Service : IService { public string Name { get; } = typeof(T).Name; } + + partial class Composition + { + void Setup() => DI.Setup(nameof(Composition)) + .Hint(Hint.Resolve, "Off") + .Bind>().To>() + .Root>("GetService", kind: RootKinds.Light); + } + + class Program + { + static void Main() + { + var composition = new Composition(); + Console.WriteLine(composition.GetService().Name); + } + } + """.RunAsync(new Options(LanguageVersion.Preview)); + + // Then + result.Success.ShouldBeTrue(result); + result.StdOut.ShouldBe(["Int32"], result); + } + + [Fact] + public async Task ShouldSupportGenericLightweightRootWithNewConstraint() + { + // Given + + // When + var result = await """ + using System; + using Pure.DI; + + namespace Sample; + + interface IService { int Size { get; } } + class Service : IService { public int Size { get; } = 1; } + class RefType { } + + partial class Composition + { + void Setup() => DI.Setup(nameof(Composition)) + .Hint(Hint.Resolve, "Off") + .Bind>().To>() + .Root>("GetService", kind: RootKinds.Light); + } + + class Program + { + static void Main() + { + var composition = new Composition(); + Console.WriteLine(composition.GetService().Size); + } + } + """.RunAsync(new Options(LanguageVersion.Preview)); + + // Then + result.Success.ShouldBeTrue(result); + result.StdOut.ShouldBe(["1"], result); + } + + [Fact] + public async Task ShouldSupportGenericLightweightRootWithArgument() + { + // Given + + // When + var result = await """ + using System; + using Pure.DI; + + namespace Sample; + + interface IService { string Value { get; } } + class Service : IService { public string Value { get; } = typeof(T).Name; } + + partial class Composition + { + void Setup() => DI.Setup(nameof(Composition)) + .Hint(Hint.Resolve, "Off") + .Bind>().To>() + .Root>("GetService", kind: RootKinds.Light); + } + + class Program + { + static void Main() + { + var composition = new Composition(); + Console.WriteLine(composition.GetService().Value); + } + } + """.RunAsync(new Options(LanguageVersion.Preview)); + + // Then + result.Success.ShouldBeTrue(result); + result.StdOut.ShouldBe(["String"], result); + } + + [Fact] + public async Task ShouldSupportGenericLightweightRootWithTwoArguments() + { + // Given + + // When + var result = await """ + using System; + using Pure.DI; + + namespace Sample; + + interface IService { string Value { get; } } + class Service : IService + { + public string Value { get; } = typeof(T).Name; + } + + partial class Composition + { + void Setup() => DI.Setup(nameof(Composition)) + .Hint(Hint.Resolve, "Off") + .Bind>().To>() + .Root>("GetService", kind: RootKinds.Light); + } + + class Program + { + static void Main() + { + var composition = new Composition(); + Console.WriteLine(composition.GetService().Value); + } + } + """.RunAsync(new Options(LanguageVersion.Preview)); + + // Then + result.Success.ShouldBeTrue(result); + result.StdOut.ShouldBe(["Int32"], result); + } + + [Fact] + public async Task ShouldSupportGenericLightweightRootWithDifferentMarkersAndArguments() + { + // Given + + // When + var result = await """ + using System; + using Pure.DI; + + namespace Sample; + + interface IService { string Value { get; } } + class Service : IService + { + public string Value { get; } = typeof(T).Name; + } + + partial class Composition + { + void Setup() => DI.Setup(nameof(Composition)) + .Hint(Hint.Resolve, "Off") + .Bind>().To>() + .Root>("GetService", kind: RootKinds.Light); + } + + class Program + { + static void Main() + { + var composition = new Composition(); + Console.WriteLine(composition.GetService().Value); + } + } + """.RunAsync(new Options(LanguageVersion.Preview)); + + // Then + result.Success.ShouldBeTrue(result); + result.StdOut.ShouldBe(["Int32"], result); + } + + [Fact] + public async Task ShouldSupportGenericLightweightRootWithTwoTypeParametersAndTwoArguments() + { + // Given + + // When + var result = await """ + using System; + using Pure.DI; + + namespace Sample; + + interface IService { string Value { get; } } + class Service : IService + { + public string Value { get; } = $"{typeof(T1).Name}:{typeof(T2).Name}"; + } + + partial class Composition + { + void Setup() => DI.Setup(nameof(Composition)) + .Hint(Hint.Resolve, "Off") + .Bind>().To>() + .Root>("GetService", kind: RootKinds.Light); + } + + class Program + { + static void Main() + { + var composition = new Composition(); + Console.WriteLine(composition.GetService().Value); + } + } + """.RunAsync(new Options(LanguageVersion.Preview)); + + // Then + result.Success.ShouldBeTrue(result); + result.StdOut.ShouldBe(["Int32:Int64"], result); + } + + [Fact] + public async Task ShouldSupportStaticLightweightRootWithArguments() + { + // Given + + // When + var result = await """ + using System; + using Pure.DI; + + namespace Sample; + + interface IService { int Id { get; } } + class Service(int id) : IService { public int Id { get; } = id; } + + partial class Composition + { + void Setup() => DI.Setup(nameof(Composition)) + .Hint(Hint.Resolve, "Off") + .RootArg("id") + .Bind().To() + .Root("GetService", kind: RootKinds.Static | RootKinds.Light); + } + + class Program + { + static void Main() + { + Console.WriteLine(Composition.GetService(77).Id); + } + } + """.RunAsync(new Options(LanguageVersion.Preview)); + + // Then + result.Success.ShouldBeTrue(result); + result.StdOut.ShouldBe(["77"], result); + } + + [Fact] + public async Task ShouldSupportStaticGenericLightweightRoot() + { + // Given + + // When + var result = await """ + using System; + using Pure.DI; + + namespace Sample; + + interface IService { string Name { get; } } + class Service : IService { public string Name { get; } = typeof(T).Name; } + + partial class Composition + { + void Setup() => DI.Setup(nameof(Composition)) + .Hint(Hint.Resolve, "Off") + .Bind>().To>() + .Root>("GetService", kind: RootKinds.Static | RootKinds.Light); + } + + class Program + { + static void Main() + { + Console.WriteLine(Composition.GetService().Name); + } + } + """.RunAsync(new Options(LanguageVersion.Preview)); + + // Then + result.Success.ShouldBeTrue(result); + result.StdOut.ShouldBe(["Decimal"], result); + } + + [Fact] + public async Task ShouldSupportExposedLightweightRootWithArguments() + { + // Given + + // When + var result = await """ + using System; + using Pure.DI; + + namespace Sample; + + interface IService { int Id { get; } } + class Service(int id) : IService { public int Id { get; } = id; } + + partial class Composition + { + void Setup() => DI.Setup(nameof(Composition)) + .Hint(Hint.Resolve, "Off") + .RootArg("id") + .Bind().To() + .Root("GetService", kind: RootKinds.Exposed | RootKinds.Light); + } + + class Program + { + static void Main() + { + var composition = new Composition(); + Console.WriteLine(composition.GetService(6).Id); + } + } + """.RunAsync(new Options(LanguageVersion.Preview)); + + // Then + result.Success.ShouldBeTrue(result); + result.StdOut.ShouldBe(["6"], result); + } + + [Fact] + public async Task ShouldSupportExposedGenericLightweightRoot() + { + // Given + + // When + var result = await """ + using System; + using Pure.DI; + + namespace Sample; + + interface IService { string Name { get; } } + class Service : IService { public string Name { get; } = typeof(T).Name; } + + partial class Composition + { + void Setup() => DI.Setup(nameof(Composition)) + .Hint(Hint.Resolve, "Off") + .Bind>().To>() + .Root>("GetService", kind: RootKinds.Exposed | RootKinds.Light); + } + + class Program + { + static void Main() + { + var composition = new Composition(); + Console.WriteLine(composition.GetService().Name); + } + } + """.RunAsync(new Options(LanguageVersion.Preview)); + + // Then + result.Success.ShouldBeTrue(result); + result.StdOut.ShouldBe(["Guid"], result); + } + + [Fact] + public async Task ShouldSupportPublicLightweightRootWithArguments() + { + // Given + + // When + var result = await """ + using System; + using Pure.DI; + + namespace Sample; + + public interface IService { int Id { get; } } + public class Service(int id) : IService { public int Id { get; } = id; } + + public partial class Composition + { + void Setup() => DI.Setup(nameof(Composition)) + .Hint(Hint.Resolve, "Off") + .RootArg("id") + .Bind().To() + .Root("Create", kind: RootKinds.Public | RootKinds.Light); + } + + class Program + { + static void Main() + { + var composition = new Composition(); + Console.WriteLine(composition.Create(4).Id); + } + } + """.RunAsync(new Options(LanguageVersion.Preview)); + + // Then + result.Success.ShouldBeTrue(result); + result.StdOut.ShouldBe(["4"], result); + } + + [Fact] + public async Task ShouldSupportInternalLightweightRootWithArguments() + { + // Given + + // When + var result = await """ + using System; + using Pure.DI; + + namespace Sample; + + interface IService { int Id { get; } } + class Service(int id) : IService { public int Id { get; } = id; } + + partial class Composition + { + void Setup() => DI.Setup(nameof(Composition)) + .Hint(Hint.Resolve, "Off") + .RootArg("id") + .Bind().To() + .Root("Create", kind: RootKinds.Internal | RootKinds.Light); + } + + class Program + { + static void Main() + { + var composition = new Composition(); + Console.WriteLine(composition.Create(11).Id); + } + } + """.RunAsync(new Options(LanguageVersion.Preview)); + + // Then + result.Success.ShouldBeTrue(result); + result.StdOut.ShouldBe(["11"], result); + } + + [Fact] + public async Task ShouldSupportPrivateLightweightRootWithArguments() + { + // Given + + // When + var result = await """ + using System; + using Pure.DI; + + namespace Sample; + + interface IService { int Id { get; } } + class Service(int id) : IService { public int Id { get; } = id; } + + partial class Composition + { + void Setup() => DI.Setup(nameof(Composition)) + .Hint(Hint.Resolve, "Off") + .RootArg("id") + .Bind().To() + .Root("Create", kind: RootKinds.Private | RootKinds.Light); + + public int UsePrivate(int id) => Create(id).Id; + } + + class Program + { + static void Main() + { + var composition = new Composition(); + Console.WriteLine(composition.UsePrivate(12)); + } + } + """.RunAsync(new Options(LanguageVersion.Preview)); + + // Then + result.Success.ShouldBeTrue(result); + result.StdOut.ShouldBe(["12"], result); + } + + [Fact] + public async Task ShouldSupportProtectedLightweightRootWithArguments() + { + // Given + + // When + var result = await """ + using System; + using Pure.DI; + + namespace Sample; + + interface IService { int Id { get; } } + class Service(int id) : IService { public int Id { get; } = id; } + + partial class Composition + { + void Setup() => DI.Setup(nameof(Composition)) + .Hint(Hint.Resolve, "Off") + .RootArg("id") + .Bind().To() + .Root("Create", kind: RootKinds.Protected | RootKinds.Light); + } + + class DerivedComposition : Composition + { + public int UseProtected(int id) => Create(id).Id; + } + + class Program + { + static void Main() + { + var composition = new DerivedComposition(); + Console.WriteLine(composition.UseProtected(13)); + } + } + """.RunAsync(new Options(LanguageVersion.Preview)); + + // Then + result.Success.ShouldBeTrue(result); + result.StdOut.ShouldBe(["13"], result); + } + + [Fact] + public async Task ShouldSupportLightweightRootWithMaximumFuncArguments() + { + // Given + + // When + var result = await """ + using System; + using Pure.DI; + + namespace Sample; + + interface IService { int Sum { get; } } + class Service( + [Tag("a1")] int a1, + [Tag("a2")] int a2, + [Tag("a3")] int a3, + [Tag("a4")] int a4, + [Tag("a5")] int a5, + [Tag("a6")] int a6, + [Tag("a7")] int a7, + [Tag("a8")] int a8, + [Tag("a9")] int a9, + [Tag("a10")] int a10, + [Tag("a11")] int a11, + [Tag("a12")] int a12, + [Tag("a13")] int a13, + [Tag("a14")] int a14, + [Tag("a15")] int a15, + [Tag("a16")] int a16) + : IService + { + public int Sum { get; } = + a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10 + a11 + a12 + a13 + a14 + a15 + a16; + } + + partial class Composition + { + void Setup() => DI.Setup(nameof(Composition)) + .Hint(Hint.Resolve, "Off") + .RootArg("a1", "a1") + .RootArg("a2", "a2") + .RootArg("a3", "a3") + .RootArg("a4", "a4") + .RootArg("a5", "a5") + .RootArg("a6", "a6") + .RootArg("a7", "a7") + .RootArg("a8", "a8") + .RootArg("a9", "a9") + .RootArg("a10", "a10") + .RootArg("a11", "a11") + .RootArg("a12", "a12") + .RootArg("a13", "a13") + .RootArg("a14", "a14") + .RootArg("a15", "a15") + .RootArg("a16", "a16") + .Bind().To() + .Root("Create", kind: RootKinds.Light); + } + + class Program + { + static void Main() + { + var composition = new Composition(); + Console.WriteLine(composition.Create(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16).Sum); + } + } + """.RunAsync(new Options(LanguageVersion.Preview)); + + // Then + result.Success.ShouldBeTrue(result); + result.StdOut.ShouldBe(["136"], result); + } + + + [Theory] + [InlineData(1)] + [InlineData(2)] + [InlineData(3)] + [InlineData(4)] + [InlineData(5)] + [InlineData(6)] + [InlineData(7)] + [InlineData(8)] + [InlineData(9)] + [InlineData(10)] + [InlineData(11)] + [InlineData(12)] + [InlineData(13)] + [InlineData(14)] + [InlineData(15)] + [InlineData(16)] + [InlineData(17)] + [InlineData(18)] + [InlineData(19)] + [InlineData(20)] + [InlineData(21)] + [InlineData(22)] + [InlineData(23)] + [InlineData(24)] + [InlineData(25)] + [InlineData(26)] + [InlineData(27)] + [InlineData(28)] + [InlineData(29)] + [InlineData(30)] + public async Task ShouldSupportLightweightRootWithTaggedIntArgumentScenarios(int value) + { + // Given + + // When + var result = await $$""" + using System; + using Pure.DI; + + namespace Sample; + + interface IService + { + int Value { get; } + int DoubleValue { get; } + } + + class Service([Tag("input")] int value) : IService + { + public int Value { get; } = value; + + public int DoubleValue { get; } = value * 2; + } + + partial class Composition + { + void Setup() => DI.Setup(nameof(Composition)) + .Hint(Hint.Resolve, "Off") + .RootArg("value", "input") + .Bind().To() + .Root("Create", kind: RootKinds.Light); + } + + class Program + { + static void Main() + { + var composition = new Composition(); + var service = composition.Create({{value}}); + Console.WriteLine($"{service.Value}:{service.DoubleValue}"); + } + } + """.RunAsync(new Options(LanguageVersion.Preview)); + + // Then + result.Success.ShouldBeTrue(result); + result.StdOut.ShouldBe([$"{value}:{value * 2}"], result); + } + + [Fact] + public async Task ShouldFailWhenCallingLightweightRootWithWrongArgumentType() + { + // Given + + // When + var result = await """ + using Pure.DI; + + namespace Sample; + + interface IService { } + class Service(int id) : IService { } + + partial class Composition + { + void Setup() => DI.Setup(nameof(Composition)) + .Hint(Hint.Resolve, "Off") + .RootArg("id") + .Bind().To() + .Root("Create", kind: RootKinds.Light); + } + + class Program + { + static void Main() + { + var composition = new Composition(); + var service = composition.Create("wrong"); + } + } + """.RunAsync(new Options(LanguageVersion.Preview, CheckCompilationErrors: false)); + + // Then + result.Success.ShouldBeFalse(result); + } + + [Fact] + public async Task ShouldFailWhenCallingLightweightRootWithoutRequiredArgument() + { + // Given + + // When + var result = await """ + using Pure.DI; + + namespace Sample; + + interface IService { } + class Service(int id) : IService { } + + partial class Composition + { + void Setup() => DI.Setup(nameof(Composition)) + .Hint(Hint.Resolve, "Off") + .RootArg("id") + .Bind().To() + .Root("Create", kind: RootKinds.Light); + } + + class Program + { + static void Main() + { + var composition = new Composition(); + var service = composition.Create(); + } + } + """.RunAsync(new Options(LanguageVersion.Preview, CheckCompilationErrors: false)); + + // Then + result.Success.ShouldBeFalse(result); + } + + [Fact] + public async Task ShouldFailWhenGenericConstraintIsViolatedForLightweightRoot() + { + // Given + + // When + var result = await """ + using Pure.DI; + + namespace Sample; + + interface IService where T : class { } + class Service : IService where T : class { } + + partial class Composition + { + void Setup() => DI.Setup(nameof(Composition)) + .Hint(Hint.Resolve, "Off") + .Bind>().To>() + .Root>("GetService", kind: RootKinds.Light); + } + + class Program + { + static void Main() + { + var composition = new Composition(); + var service = composition.GetService(); + } + } + """.RunAsync(new Options(LanguageVersion.Preview, CheckCompilationErrors: false)); + + // Then + result.Success.ShouldBeFalse(result); + } +}