D20Tek.Spectre.Console.Extensions.MoreContainers
1.50.1.7
dotnet add package D20Tek.Spectre.Console.Extensions.MoreContainers --version 1.50.1.7
NuGet\Install-Package D20Tek.Spectre.Console.Extensions.MoreContainers -Version 1.50.1.7
<PackageReference Include="D20Tek.Spectre.Console.Extensions.MoreContainers" Version="1.50.1.7" />
<PackageVersion Include="D20Tek.Spectre.Console.Extensions.MoreContainers" Version="1.50.1.7" />
<PackageReference Include="D20Tek.Spectre.Console.Extensions.MoreContainers" />
paket add D20Tek.Spectre.Console.Extensions.MoreContainers --version 1.50.1.7
#r "nuget: D20Tek.Spectre.Console.Extensions.MoreContainers, 1.50.1.7"
#:package D20Tek.Spectre.Console.Extensions.MoreContainers@1.50.1.7
#addin nuget:?package=D20Tek.Spectre.Console.Extensions.MoreContainers&version=1.50.1.7
#tool nuget:?package=D20Tek.Spectre.Console.Extensions.MoreContainers&version=1.50.1.7
d20Tek Spectre.Console Extensions
Introduction
This package provides extensions for common code and patterns when using Spectre.Console CLI app framework SpectreConsole.
The current releases contain implementations of ITypeRegistrar and ITypeResolver for the following DI frameworks:
- Microsoft.Extensions.DependencyInjection
- Autofac
- Lamar
- LightInject
- Ninject
We also support the CommandAppBuilder to easily create, configure, and run your instance of Spectre.Console.CommandApp.
The new Spectre.Console.Extensions.Testing namespace supports test infrastructure classes to easily test commands, configuration, and end-to-end functionaly runs. There are various CommandAppTextContext classes and helpers that simplify unit test boilerplate code.
Additional Spectre Controls:
- CurrencyPrompt - TextPrompt (culture-aware) for currency input validation and conversion to decimal value.
- CurrencyPresenter - show currency in a culture-aware way, including abbreviations for large values.
- HistoryTextPrompt<T> - duplicates TextPrompt and add ability to remember previous entries and use arrow up/down keys to navigate the list.
- Table extension to add a separator row.
Note: Only Microsoft.Extensions.DependencyInjection is implemented in the core extensions package (D20Tek.Spectre.Console.Extensions). The other DI containers have been repackaged into D20Tek.Spectre.Console.Extensions.MoreContainers, so that we could minimize the dependencies of the core package, and only add those dependencies for users that want to use one of those other frameworks. And, our TypeRegistrars continue to work for those different frameworks.
For future releases, I will continue to investigate integration with other DI frameworks and logging integrations.
Installation
This libraries are NuGet packages so they are easy to add to your project. To install these packages into your solution, you can use the NuGet Package Manager. In PM, please use the following command:
PM > Install-Package D20Tek.Spectre.Console.Extensions -Version 1.50.7
PM > Install-Package D20Tek.Spectre.Console.Extensions.MoreContainers -Version 1.50.7
To install in the Visual Studio UI, go to the Tools menu > "Manage NuGet Packages". Then search for D20Tek.Spectre.Console.Extensions and install it from there.
Read more about the current release in our Release Notes.
Usage
Once you've installed the NuGet package, you can start using it in your Spectre.Console projects. If you would like basic information about how to build Spectre.Console CommandApps, please read: https://darthpedro.net/lessons-cli/.
With CommandAppBuilder [recommended]
To add dependency injection into a CommandApp using the CommandAppBuilder, you can do the following in Program.cs:
using D20Tek.Samples.Common.Commands;
using D20Tek.Spectre.Console.Extensions;
namespace DependencyInjection.Cli
{
public class Program
{
public static async Task<int> Main(string[] args)
{
return await new CommandAppBuilder()
.WithDIContainer()
.WithStartup<Startup>()
.WithDefaultCommand<DefaultCommand>()
.Build()
.RunAsync(args);
}
}
}
And, you will need to create the following Startup.cs file:
using D20Tek.Samples.Common.Commands;
using D20Tek.Samples.Common.Services;
using D20Tek.Spectre.Console.Extensions;
using Spectre.Console.Cli;
namespace DependencyInjection.Cli
{
internal class Startup : StartupBase
{
public override void ConfigureServices(ITypeRegistrar registrar)
{
// register services here...
registrar.Register(typeof(IDisplayWriter), typeof(ConsoleDisplayWriter));
// or use registration extensions on WithLifetimes() to specify the container's ServiceLifetime.
// registrar.WithLifetimes().RegisterSingleton<IDisplayWriter, ConsoleDisplayWriter>();
}
public override IConfigurator ConfigureCommands(IConfigurator config)
{
config.CaseSensitivity(CaseSensitivity.None);
config.SetApplicationName("DependencyInjection.Cli");
config.ValidateExamples();
config.AddCommand<DefaultCommand>("default")
.WithDescription("Default command that displays some text.")
.WithExample(new[] { "default", "--verbose", "high" });
return config;
}
}
}
With Custom Code in Program
You do not need to use the CommandAppBuilder. It is still possible to write custom code your Program.Main method. And that can be simpler for small console applications.
To add dependency injection this way, you can do the following:
using D20Tek.Samples.Common.Commands;
using D20Tek.Samples.Common.Services;
using D20Tek.Spectre.Console.Extensions.Injection;
using Microsoft.Extensions.DependencyInjection;
using Spectre.Console.Cli;
namespace D20Tek.CountryService.Cli
{
public class Program
{
public static async Task<int> Main(string[] args)
{
// Create the DI container.
var services = new ServiceCollection();
// configure services here...
services.AddSingleton<IDisplayWriter, ConsoleDisplayWriter>();
var registrar = new DependencyInjectionTypeRegistrar(services);
// Create the CommandApp with specified command type and type registrar.
var app = new CommandApp<DefaultCommand>(registrar);
// Configure any commands in the application.
app.Configure(config =>
{
config.CaseSensitivity(CaseSensitivity.None);
config.SetApplicationName("Basic.Cli");
config.ValidateExamples();
config.AddCommand<DefaultCommand>("default")
.WithDescription("Default command that displays some text.")
.WithExample(new[] { "default", "--verbose", "high" });
});
return await app.RunAsync(args);
}
}
}
Note: these code snippets assume using the Microsoft.Extensions.DependencyInjection framework. But similar sample code also exists for the other DI frameworks.
Samples:
For more detailed examples on how to use D20Tek.Spectre.Console.Extensions, please review the following samples:
- Basic Cli with DI - full listing for code in the Usage - Custom Code section above.
- DependencyInjection.Cli - More elaborate use of Microsoft.Extensions.DependencyInjection registrar and resolver. Along with using the CommandAppBuilder to remove some of the creation complexity.
- Autofac.Cli - Use the Autofac DI framework to build type registrar and resolver.
- Lamar.Cli - Use the Lamar DI framework to build type registrar and resolver.
- LightInject.Cli - Use the LightInject DI framework to build type registrar and resolver.
- Ninject.Cli - Use the Ninject DI framework to build type registrar and resolver.
- SimpleInjector.Cli - Use the SimpleInjector DI framework to build type registrar and resolver.
- NoDI.Cli - Use the CommandAppBuilder to configure a console app that does not use a DI framework.
- InteractivePrompt.Cli - Create an interactive prompt that can run other registered commands while remaining in the prompt.
Testing Infrastructure
This library also provides testing classes that help in building your CommandApp unit tests. Using the CommandAppTestContext allows you to easily configure and run commands in isolation. Here is an example of a simple command unit test written in VSTest (though these test contexts will work in any test framework):
//---------------------------------------------------------------------------------------------------------------------
// Copyright (c) d20Tek. All rights reserved.
//---------------------------------------------------------------------------------------------------------------------
using D20Tek.Spectre.Console.Extensions.Testing;
using D20Tek.Spectre.Console.Extensions.UnitTests.Mocks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Spectre.Console.Cli;
using System;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
namespace D20Tek.Spectre.Console.Extensions.UnitTests.Testing
{
[TestClass]
public class CommandAppTestContextTests
{
[TestMethod]
public void Run()
{
// arrange
var context = new CommandAppTestContext();
context.Configure(config =>
{
config.Settings.ApplicationName = "Run Test 1";
config.AddCommand<MockCommand>("test");
});
// act
var result = context.Run(new string[] { "test" });
// assert
Assert.IsNotNull(result);
Assert.AreEqual(0, result.ExitCode);
StringAssert.Contains(result.Output, "Success");
Assert.AreEqual("test", result.Context.Name);
Assert.IsInstanceOfType(result.Settings, typeof(EmptyCommandSettings));
}
}
}
Feedback
If you use these libraries and have any feedback, bugs, or suggestions, please file them in the Issues section of this repository.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net9.0
- Autofac (>= 8.3.0)
- D20Tek.Spectre.Console.Extensions (>= 1.50.1.7)
- Lamar (>= 15.0.1)
- LightInject (>= 7.0.1)
- Ninject (>= 3.3.6)
- SimpleInjector (>= 5.5.0)
- Spectre.Console (>= 0.50.0)
- Spectre.Console.Cli (>= 0.50.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Split off additional dependency injection containers into this package to minimize dependencies in the core package.