mirror of
https://github.com/Tyrrrz/CliFx.git
synced 2025-10-25 15:19:17 +00:00
Add an overload of UseTypeActivator(...) that takes a list of added command types
This commit is contained in:
@@ -1,25 +1,21 @@
|
|||||||
using CliFx;
|
using CliFx;
|
||||||
using CliFx.Demo.Commands;
|
|
||||||
using CliFx.Demo.Domain;
|
using CliFx.Demo.Domain;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
// We use Microsoft.Extensions.DependencyInjection for injecting dependencies in commands
|
|
||||||
var services = new ServiceCollection();
|
|
||||||
|
|
||||||
// Register services
|
|
||||||
services.AddSingleton<LibraryProvider>();
|
|
||||||
|
|
||||||
// Register commands
|
|
||||||
services.AddTransient<BookCommand>();
|
|
||||||
services.AddTransient<BookAddCommand>();
|
|
||||||
services.AddTransient<BookRemoveCommand>();
|
|
||||||
services.AddTransient<BookListCommand>();
|
|
||||||
|
|
||||||
var serviceProvider = services.BuildServiceProvider();
|
|
||||||
|
|
||||||
return await new CliApplicationBuilder()
|
return await new CliApplicationBuilder()
|
||||||
.SetDescription("Demo application showcasing CliFx features.")
|
.SetDescription("Demo application showcasing CliFx features.")
|
||||||
.AddCommandsFromThisAssembly()
|
.AddCommandsFromThisAssembly()
|
||||||
.UseTypeActivator(serviceProvider.GetRequiredService)
|
.UseTypeActivator(commandTypes =>
|
||||||
|
{
|
||||||
|
// We use Microsoft.Extensions.DependencyInjection for injecting dependencies in commands
|
||||||
|
var services = new ServiceCollection();
|
||||||
|
services.AddSingleton<LibraryProvider>();
|
||||||
|
|
||||||
|
// Register all commands as transient services
|
||||||
|
foreach (var commandType in commandTypes)
|
||||||
|
services.AddTransient(commandType);
|
||||||
|
|
||||||
|
return services.BuildServiceProvider();
|
||||||
|
})
|
||||||
.Build()
|
.Build()
|
||||||
.RunAsync();
|
.RunAsync();
|
||||||
@@ -156,14 +156,23 @@ public class TypeActivationSpecs : SpecsBase
|
|||||||
"""
|
"""
|
||||||
);
|
);
|
||||||
|
|
||||||
var serviceProvider = new ServiceCollection()
|
|
||||||
.AddSingleton(commandType, Activator.CreateInstance(commandType, "Hello world")!)
|
|
||||||
.BuildServiceProvider();
|
|
||||||
|
|
||||||
var application = new CliApplicationBuilder()
|
var application = new CliApplicationBuilder()
|
||||||
.AddCommand(commandType)
|
.AddCommand(commandType)
|
||||||
.UseConsole(FakeConsole)
|
.UseConsole(FakeConsole)
|
||||||
.UseTypeActivator(serviceProvider)
|
.UseTypeActivator(commandTypes =>
|
||||||
|
{
|
||||||
|
var services = new ServiceCollection();
|
||||||
|
|
||||||
|
foreach (var serviceType in commandTypes)
|
||||||
|
{
|
||||||
|
services.AddSingleton(
|
||||||
|
serviceType,
|
||||||
|
Activator.CreateInstance(serviceType, "Hello world")!
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return services.BuildServiceProvider();
|
||||||
|
})
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ public partial class CliApplicationBuilder
|
|||||||
public CliApplicationBuilder AddCommand(Type commandType)
|
public CliApplicationBuilder AddCommand(Type commandType)
|
||||||
{
|
{
|
||||||
_commandTypes.Add(commandType);
|
_commandTypes.Add(commandType);
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,7 +111,7 @@ public partial class CliApplicationBuilder
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets application title, which is shown in the help text.
|
/// Sets the application title, which is shown in the help text.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// By default, application title is inferred from the assembly name.
|
/// By default, application title is inferred from the assembly name.
|
||||||
@@ -124,11 +123,10 @@ public partial class CliApplicationBuilder
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets application executable name, which is shown in the help text.
|
/// Sets the application executable name, which is shown in the help text.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// By default, application executable name is inferred from the assembly file name.
|
/// By default, application executable name is inferred from the assembly file name.
|
||||||
/// The file name is also prefixed with `dotnet` if it's a DLL file.
|
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public CliApplicationBuilder SetExecutableName(string executableName)
|
public CliApplicationBuilder SetExecutableName(string executableName)
|
||||||
{
|
{
|
||||||
@@ -137,8 +135,7 @@ public partial class CliApplicationBuilder
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets application version, which is shown in the help text or
|
/// Sets the application version, which is shown in the help text or when the user specifies the version option.
|
||||||
/// when the user specifies the version option.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// By default, application version is inferred from the assembly version.
|
/// By default, application version is inferred from the assembly version.
|
||||||
@@ -150,7 +147,7 @@ public partial class CliApplicationBuilder
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets application description, which is shown in the help text.
|
/// Sets the application description, which is shown in the help text.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public CliApplicationBuilder SetDescription(string? description)
|
public CliApplicationBuilder SetDescription(string? description)
|
||||||
{
|
{
|
||||||
@@ -177,10 +174,10 @@ public partial class CliApplicationBuilder
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Configures the application to use the specified function for activating types.
|
/// Configures the application to use the specified delegate for activating types.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public CliApplicationBuilder UseTypeActivator(Func<Type, object> typeActivator) =>
|
public CliApplicationBuilder UseTypeActivator(Func<Type, object> createInstance) =>
|
||||||
UseTypeActivator(new DelegateTypeActivator(typeActivator));
|
UseTypeActivator(new DelegateTypeActivator(createInstance));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Configures the application to use the specified service provider for activating types.
|
/// Configures the application to use the specified service provider for activating types.
|
||||||
@@ -188,6 +185,14 @@ public partial class CliApplicationBuilder
|
|||||||
public CliApplicationBuilder UseTypeActivator(IServiceProvider serviceProvider) =>
|
public CliApplicationBuilder UseTypeActivator(IServiceProvider serviceProvider) =>
|
||||||
UseTypeActivator(serviceProvider.GetService);
|
UseTypeActivator(serviceProvider.GetService);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Configures the application to use the specified service provider for activating types.
|
||||||
|
/// This method takes a delegate that receives the list of all added command types, so that you can
|
||||||
|
/// easily register them with the service provider.
|
||||||
|
/// </summary>
|
||||||
|
public CliApplicationBuilder UseTypeActivator(Func<IReadOnlyList<Type>, IServiceProvider> getServiceProvider) =>
|
||||||
|
UseTypeActivator(getServiceProvider(_commandTypes.ToArray()));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a configured instance of <see cref="CliApplication" />.
|
/// Creates a configured instance of <see cref="CliApplication" />.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -221,45 +226,56 @@ public partial class CliApplicationBuilder
|
|||||||
{
|
{
|
||||||
var entryAssemblyName = EnvironmentEx.EntryAssembly?.GetName().Name;
|
var entryAssemblyName = EnvironmentEx.EntryAssembly?.GetName().Name;
|
||||||
if (string.IsNullOrWhiteSpace(entryAssemblyName))
|
if (string.IsNullOrWhiteSpace(entryAssemblyName))
|
||||||
return "App";
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"Failed to infer the default application title. " +
|
||||||
|
$"Please specify it explicitly using {nameof(SetTitle)}()."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return entryAssemblyName;
|
return entryAssemblyName;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetDefaultExecutableName()
|
private static string GetDefaultExecutableName()
|
||||||
{
|
{
|
||||||
var entryAssemblyLocation = EnvironmentEx.EntryAssembly?.Location;
|
var entryAssemblyFilePath = EnvironmentEx.EntryAssembly?.Location;
|
||||||
if (string.IsNullOrWhiteSpace(entryAssemblyLocation))
|
var processFilePath = EnvironmentEx.ProcessPath;
|
||||||
return "app";
|
|
||||||
|
|
||||||
// If the application was launched via matching EXE apphost, use that as the executable name
|
if (string.IsNullOrWhiteSpace(entryAssemblyFilePath) || string.IsNullOrWhiteSpace(processFilePath))
|
||||||
var isLaunchedViaAppHost = string.Equals(
|
{
|
||||||
EnvironmentEx.ProcessPath,
|
throw new InvalidOperationException(
|
||||||
Path.ChangeExtension(entryAssemblyLocation, ".exe"),
|
"Failed to infer the default application executable name. " +
|
||||||
StringComparison.OrdinalIgnoreCase
|
$"Please specify it explicitly using {nameof(SetExecutableName)}()."
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (isLaunchedViaAppHost)
|
// If the process path matches the entry assembly path, it's a legacy .NET Framework app
|
||||||
return Path.GetFileNameWithoutExtension(entryAssemblyLocation);
|
// or a self-contained .NET Core app.
|
||||||
|
if (PathEx.AreEqual(entryAssemblyFilePath, processFilePath))
|
||||||
|
return Path.GetFileNameWithoutExtension(entryAssemblyFilePath);
|
||||||
|
|
||||||
// Otherwise, use the entry assembly as the executable name.
|
// If the process path has the same name and parent directory as the entry assembly path,
|
||||||
// Prefix it with `dotnet` if it's a DLL file.
|
// but different extension, it's a framework-dependent .NET Core app launched through the apphost.
|
||||||
var isDll = string.Equals(
|
if (PathEx.AreEqual(Path.ChangeExtension(entryAssemblyFilePath, ".exe"), processFilePath) ||
|
||||||
Path.GetExtension(entryAssemblyLocation),
|
PathEx.AreEqual(Path.ChangeExtension(entryAssemblyFilePath, ""), processFilePath))
|
||||||
".dll",
|
{
|
||||||
StringComparison.OrdinalIgnoreCase
|
return Path.GetFileNameWithoutExtension(entryAssemblyFilePath);
|
||||||
);
|
}
|
||||||
|
|
||||||
return isDll
|
// Otherwise, it's a framework-dependent .NET Core app launched through the .NET CLI
|
||||||
? "dotnet " + Path.GetFileName(entryAssemblyLocation)
|
return "dotnet " + Path.GetFileName(entryAssemblyFilePath);
|
||||||
: Path.GetFileNameWithoutExtension(entryAssemblyLocation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetDefaultVersionText()
|
private static string GetDefaultVersionText()
|
||||||
{
|
{
|
||||||
var entryAssemblyVersion = EnvironmentEx.EntryAssembly?.GetName().Version;
|
var entryAssemblyVersion = EnvironmentEx.EntryAssembly?.GetName().Version;
|
||||||
if (entryAssemblyVersion is null)
|
if (entryAssemblyVersion is null)
|
||||||
return "v1.0";
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"Failed to infer the default application version. " +
|
||||||
|
$"Please specify it explicitly using {nameof(SetVersion)}()."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return "v" + entryAssemblyVersion.ToSemanticString();
|
return "v" + entryAssemblyVersion.ToSemanticString();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,22 +4,22 @@ using CliFx.Exceptions;
|
|||||||
namespace CliFx.Infrastructure;
|
namespace CliFx.Infrastructure;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Implementation of <see cref="ITypeActivator" /> that instantiates an object
|
/// Implementation of <see cref="ITypeActivator" /> that instantiates an object by using a predefined delegate.
|
||||||
/// by using a predefined function.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class DelegateTypeActivator : ITypeActivator
|
public class DelegateTypeActivator : ITypeActivator
|
||||||
{
|
{
|
||||||
private readonly Func<Type, object> _func;
|
private readonly Func<Type, object> _createInstance;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes an instance of <see cref="DelegateTypeActivator" />.
|
/// Initializes an instance of <see cref="DelegateTypeActivator" />.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DelegateTypeActivator(Func<Type, object> func) => _func = func;
|
public DelegateTypeActivator(Func<Type, object> createInstance) =>
|
||||||
|
_createInstance = createInstance;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public object CreateInstance(Type type)
|
public object CreateInstance(Type type)
|
||||||
{
|
{
|
||||||
var instance = _func(type);
|
var instance = _createInstance(type);
|
||||||
|
|
||||||
if (instance is null)
|
if (instance is null)
|
||||||
{
|
{
|
||||||
|
|||||||
22
CliFx/Utils/PathEx.cs
Normal file
22
CliFx/Utils/PathEx.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace CliFx.Utils;
|
||||||
|
|
||||||
|
internal static class PathEx
|
||||||
|
{
|
||||||
|
private static StringComparer EqualityComparer { get; } =
|
||||||
|
RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
|
||||||
|
? StringComparer.OrdinalIgnoreCase
|
||||||
|
: StringComparer.Ordinal;
|
||||||
|
|
||||||
|
public static bool AreEqual(string path1, string path2)
|
||||||
|
{
|
||||||
|
static string Normalize(string path) => Path
|
||||||
|
.GetFullPath(path)
|
||||||
|
.Trim(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
||||||
|
|
||||||
|
return EqualityComparer.Equals(Normalize(path1), Normalize(path2));
|
||||||
|
}
|
||||||
|
}
|
||||||
30
Readme.md
30
Readme.md
@@ -531,24 +531,24 @@ The following example shows how to configure your application to use [`Microsoft
|
|||||||
```csharp
|
```csharp
|
||||||
public static class Program
|
public static class Program
|
||||||
{
|
{
|
||||||
public static async Task<int> Main()
|
public static async Task<int> Main() =>
|
||||||
{
|
await new CliApplicationBuilder()
|
||||||
var services = new ServiceCollection();
|
|
||||||
|
|
||||||
// Register services
|
|
||||||
services.AddSingleton<MyService>();
|
|
||||||
|
|
||||||
// Register commands
|
|
||||||
services.AddTransient<MyCommand>();
|
|
||||||
|
|
||||||
var serviceProvider = services.BuildServiceProvider();
|
|
||||||
|
|
||||||
return await new CliApplicationBuilder()
|
|
||||||
.AddCommandsFromThisAssembly()
|
.AddCommandsFromThisAssembly()
|
||||||
.UseTypeActivator(serviceProvider)
|
.UseTypeActivator(commandTypes =>
|
||||||
|
{
|
||||||
|
var services = new ServiceCollection();
|
||||||
|
|
||||||
|
// Register services
|
||||||
|
services.AddSingleton<MyService>();
|
||||||
|
|
||||||
|
// Register commands
|
||||||
|
foreach (var commandType in commandTypes)
|
||||||
|
services.AddTransient(commandType);
|
||||||
|
|
||||||
|
return services.BuildServiceProvider();
|
||||||
|
})
|
||||||
.Build()
|
.Build()
|
||||||
.RunAsync();
|
.RunAsync();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user