Skip to content

Commit

Permalink
ITypeResolver is now implicitly registered to itself & Merge TypeReso…
Browse files Browse the repository at this point in the history
…lver check
  • Loading branch information
JKamsker committed Sep 24, 2023
1 parent a911355 commit 80d5b91
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 9 deletions.
7 changes: 7 additions & 0 deletions examples/Cli/AutoCompletion/Program.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Diagnostics;
using Spectre.Console.Cli;

namespace AutoCompletion;
Expand All @@ -14,6 +15,12 @@ internal static class Program
{
private static void Main(string[] args)
{
// If we just want to test the completion with f5 in visual studio
if (Debugger.IsAttached)
{
args = new[] { "cli", "complete", "\"Li\"" };
}

var app = new CommandApp();
app.Configure(config => config.AddCommand<LionCommand>("lion"));

Expand Down
36 changes: 34 additions & 2 deletions src/Spectre.Console.Cli/Internal/CommandExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public async Task<int> Execute(IConfiguration configuration, IEnumerable<string>
_registrar.RegisterInstance(typeof(IRemainingArguments), parsedResult.Remaining);

// Create the resolver.
using (var resolver = new TypeResolverAdapter(_registrar.Build()))
using (var resolver = MakeTypeResolverAdapter(_registrar))
{
// Get the registered help provider, falling back to the default provider
// registered above if no custom implementations have been registered.
Expand Down Expand Up @@ -114,7 +114,39 @@ private CommandTreeParserResult ParseCommandLineArguments(CommandModel model, Co
}

return parsedResult;
}
}

/// <summary>
/// Builds a TypeResolver where the ITypeResolver service is registered to itself.
/// This only works when the DI framework is not as bad as the primitive one this library uses internally.
/// In case the internal one is used, we have a fallback that uses the built resolver directly.
/// </summary>
private TypeResolverAdapter MakeTypeResolverAdapter(ITypeRegistrar registrar)
{
ITypeResolver? builtResolver = null;
var isBuilt = false;

registrar.RegisterLazy(typeof(ITypeResolver), () =>
{
if (isBuilt && builtResolver is null)
{
// This should not happen, because it is build before this method is called.
throw new InvalidOperationException("The builtResolver has not been initialized.");
}
return builtResolver;
});

builtResolver = registrar.Build();

// Activates the "InvalidOperationException" if it is not initialized.
// The internal DI framework will call the lazy factory on build.
isBuilt = true;

var adapter = new TypeResolverAdapter(builtResolver);
return adapter;
}

#pragma warning restore CS8603 // Possible null reference return.

private static string ResolveApplicationVersion(IConfiguration configuration)
Expand Down
11 changes: 4 additions & 7 deletions src/Spectre.Console.Cli/Internal/Composition/Activators.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,10 @@ public override object Activate(DefaultTypeResolver container)
for (var i = 0; i < _parameters.Count; i++)
{
var parameter = _parameters[i];
if (parameter.ParameterType == typeof(DefaultTypeResolver))
var isTypeResolver = parameter.ParameterType == typeof(ITypeResolver)
|| parameter.ParameterType == typeof(DefaultTypeResolver);

if (isTypeResolver)
{
parameters[i] = container;
}
Expand All @@ -82,12 +85,6 @@ public override object Activate(DefaultTypeResolver container)
var resolved = container.Resolve(parameter.ParameterType);
if (resolved == null)
{
if (parameter.ParameterType == typeof(ITypeResolver))
{
parameters[i] = container;
continue;
}

if (!parameter.IsOptional)
{
throw new InvalidOperationException($"Could not find registration for '{parameter.ParameterType.FullName}'.");
Expand Down

0 comments on commit 80d5b91

Please sign in to comment.