8 Commits
2.2.5 ... 2.3

Author SHA1 Message Date
Oleksii Holub
5e97ebe7f0 Update version 2022-07-12 13:31:07 +03:00
Oleksii Holub
64cbdaaeab Add console dimension properties to IConsole
Closes #90
2022-06-28 15:38:10 +03:00
Oleksii Holub
ae1f03914c Update version 2022-06-14 22:34:55 +03:00
Oleksii Holub
ff25dccf8a Add overload of UseTypeActivator(...) that accepts an instance of IServiceProvider 2022-06-07 20:08:34 +03:00
Oleksii Holub
6e0d881682 Use new alerts in readme 2022-05-31 12:41:32 +03:00
Oleksii Holub
89fd42888a Update readme 2022-05-31 12:25:18 +03:00
Oleksii Holub
eeac82a6e7 Update nuget packages 2022-05-29 22:33:54 +03:00
Oleksii Holub
c641c6fbe2 Improve grammar in error messages 2022-05-26 22:21:31 +03:00
13 changed files with 149 additions and 47 deletions

View File

@@ -1,3 +1,13 @@
### v2.3 (12-Jul-2022)
- Added console dimension properties `WindowWidth` and `WindowHeight` to `IConsole` interface and implementing classes.
- Improved inline documentation for members of `IConsole` interface.
### v2.2.6 (14-Jun-2022)
- Added an overload of `CliApplicationBuilder.UseTypeActivator(...)` that accepts an instance of `IServiceProvider`. This slightly simplifies integration with many DI containers.
- Fixed minor grammar mistakes in user-facing error messages.
### v2.2.5 (10-May-2022) ### v2.2.5 (10-May-2022)
- Updated default value resolution for the application executable name. It will now resolve to `myapp.exe` instead of `dotnet myapp.dll` when the application is launched through the EXE apphost on Windows. On other platforms, or when running the application through the .NET CLI, the behavior will be the same as before. - Updated default value resolution for the application executable name. It will now resolve to `myapp.exe` instead of `dotnet myapp.dll` when the application is launched through the EXE apphost on Windows. On other platforms, or when running the application through the .NET CLI, the behavior will be the same as before.

View File

@@ -10,10 +10,10 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Basic.Reference.Assemblies" Version="1.2.4" /> <PackageReference Include="Basic.Reference.Assemblies" Version="1.2.4" />
<PackageReference Include="GitHubActionsTestLogger" Version="1.4.1" PrivateAssets="all" /> <PackageReference Include="GitHubActionsTestLogger" Version="2.0.0" PrivateAssets="all" />
<PackageReference Include="FluentAssertions" Version="6.6.0" /> <PackageReference Include="FluentAssertions" Version="6.7.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.1.0" /> <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.2.0" />
<PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5" PrivateAssets="all" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5" PrivateAssets="all" />
<PackageReference Include="coverlet.collector" Version="3.1.2" PrivateAssets="all" /> <PackageReference Include="coverlet.collector" Version="3.1.2" PrivateAssets="all" />

View File

@@ -9,7 +9,7 @@
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" /> <PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
<PackageReference Include="clipr" Version="1.6.1" /> <PackageReference Include="clipr" Version="1.6.1" />
<PackageReference Include="Cocona" Version="2.0.3" /> <PackageReference Include="Cocona" Version="2.0.3" />
<PackageReference Include="CommandLineParser" Version="2.8.0" /> <PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="4.0.1" /> <PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="4.0.1" />
<PackageReference Include="PowerArgs" Version="3.6.0" /> <PackageReference Include="PowerArgs" Version="3.6.0" />
<PackageReference Include="System.CommandLine" Version="2.0.0-beta1.20574.7" /> <PackageReference Include="System.CommandLine" Version="2.0.0-beta1.20574.7" />

View File

@@ -11,10 +11,11 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Basic.Reference.Assemblies" Version="1.2.4" /> <PackageReference Include="Basic.Reference.Assemblies" Version="1.2.4" />
<PackageReference Include="CliWrap" Version="3.4.4" /> <PackageReference Include="CliWrap" Version="3.4.4" />
<PackageReference Include="FluentAssertions" Version="6.6.0" /> <PackageReference Include="FluentAssertions" Version="6.7.0" />
<PackageReference Include="GitHubActionsTestLogger" Version="1.4.1" PrivateAssets="all" /> <PackageReference Include="GitHubActionsTestLogger" Version="2.0.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.1.0" /> <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.2.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
<PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5" PrivateAssets="all" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5" PrivateAssets="all" />
<PackageReference Include="coverlet.collector" Version="3.1.2" PrivateAssets="all" /> <PackageReference Include="coverlet.collector" Version="3.1.2" PrivateAssets="all" />

View File

@@ -57,6 +57,8 @@ public class Command : ICommand
console.ResetColor(); console.ResetColor();
console.ForegroundColor = ConsoleColor.DarkMagenta; console.ForegroundColor = ConsoleColor.DarkMagenta;
console.BackgroundColor = ConsoleColor.DarkMagenta; console.BackgroundColor = ConsoleColor.DarkMagenta;
console.WindowWidth = 100;
console.WindowHeight = 25;
console.CursorLeft = 42; console.CursorLeft = 42;
console.CursorTop = 24; console.CursorTop = 24;
@@ -90,6 +92,8 @@ public class Command : ICommand
Console.BackgroundColor.Should().NotBe(ConsoleColor.DarkMagenta); Console.BackgroundColor.Should().NotBe(ConsoleColor.DarkMagenta);
// This fails because tests don't spawn a console window // This fails because tests don't spawn a console window
//Console.WindowWidth.Should().Be(100);
//Console.WindowHeight.Should().Be(25);
//Console.CursorLeft.Should().NotBe(42); //Console.CursorLeft.Should().NotBe(42);
//Console.CursorTop.Should().NotBe(24); //Console.CursorTop.Should().NotBe(24);

View File

@@ -4,6 +4,7 @@ using System.Threading.Tasks;
using CliFx.Infrastructure; using CliFx.Infrastructure;
using CliFx.Tests.Utils; using CliFx.Tests.Utils;
using FluentAssertions; using FluentAssertions;
using Microsoft.Extensions.DependencyInjection;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
@@ -87,7 +88,7 @@ public class Command : ICommand
} }
[Fact] [Fact]
public async Task Delegate_type_activator_can_initialize_a_type_using_a_custom_function() public async Task Custom_type_activator_can_initialize_a_type_using_a_given_function()
{ {
// Arrange // Arrange
var commandType = DynamicCommandBuilder.Compile( var commandType = DynamicCommandBuilder.Compile(
@@ -110,7 +111,7 @@ public class Command : ICommand
var application = new CliApplicationBuilder() var application = new CliApplicationBuilder()
.AddCommand(commandType) .AddCommand(commandType)
.UseConsole(FakeConsole) .UseConsole(FakeConsole)
.UseTypeActivator(type => Activator.CreateInstance(type, "hello world")!) .UseTypeActivator(type => Activator.CreateInstance(type, "Hello world")!)
.Build(); .Build();
// Act // Act
@@ -123,11 +124,55 @@ public class Command : ICommand
// Assert // Assert
exitCode.Should().Be(0); exitCode.Should().Be(0);
stdOut.Trim().Should().Be("hello world"); stdOut.Trim().Should().Be("Hello world");
} }
[Fact] [Fact]
public async Task Delegate_type_activator_fails_if_the_underlying_function_returns_null() public async Task Custom_type_activator_can_initialize_a_type_using_a_service_provider()
{
// Arrange
var commandType = DynamicCommandBuilder.Compile(
// language=cs
@"
[Command]
public class Command : ICommand
{
private readonly string _foo;
public Command(string foo) => _foo = foo;
public ValueTask ExecuteAsync(IConsole console)
{
console.Output.WriteLine(_foo);
return default;
}
}");
var serviceProvider = new ServiceCollection()
.AddSingleton(commandType, Activator.CreateInstance(commandType, "Hello world")!)
.BuildServiceProvider();
var application = new CliApplicationBuilder()
.AddCommand(commandType)
.UseConsole(FakeConsole)
.UseTypeActivator(serviceProvider)
.Build();
// Act
var exitCode = await application.RunAsync(
Array.Empty<string>(),
new Dictionary<string, string>()
);
var stdOut = FakeConsole.ReadOutputString();
// Assert
exitCode.Should().Be(0);
stdOut.Trim().Should().Be("Hello world");
}
[Fact]
public async Task Custom_type_activator_fails_if_the_underlying_function_returns_null()
{ {
// Arrange // Arrange
var commandType = DynamicCommandBuilder.Compile( var commandType = DynamicCommandBuilder.Compile(

View File

@@ -182,6 +182,12 @@ public partial class CliApplicationBuilder
public CliApplicationBuilder UseTypeActivator(Func<Type, object> typeActivator) => public CliApplicationBuilder UseTypeActivator(Func<Type, object> typeActivator) =>
UseTypeActivator(new DelegateTypeActivator(typeActivator)); UseTypeActivator(new DelegateTypeActivator(typeActivator));
/// <summary>
/// Configures the application to use the specified service provider for activating types.
/// </summary>
public CliApplicationBuilder UseTypeActivator(IServiceProvider serviceProvider) =>
UseTypeActivator(serviceProvider.GetService);
/// <summary> /// <summary>
/// Creates a configured instance of <see cref="CliApplication"/>. /// Creates a configured instance of <see cref="CliApplication"/>.
/// </summary> /// </summary>

View File

@@ -169,7 +169,7 @@ internal class CommandBinder
: ex.Message; : ex.Message;
throw CliFxException.UserError( throw CliFxException.UserError(
$"{memberSchema.GetKind()} {memberSchema.GetFormattedIdentifier()} cannot be set from provided argument(s):" + $"{memberSchema.GetKind()} {memberSchema.GetFormattedIdentifier()} cannot be set from the provided argument(s):" +
Environment.NewLine + Environment.NewLine +
rawValues.Select(v => '<' + v + '>').JoinToString(" ") + rawValues.Select(v => '<' + v + '>').JoinToString(" ") +
Environment.NewLine + Environment.NewLine +

View File

@@ -42,11 +42,10 @@ public class FakeConsole : IConsole, IDisposable
public ConsoleColor BackgroundColor { get; set; } = ConsoleColor.Black; public ConsoleColor BackgroundColor { get; set; } = ConsoleColor.Black;
/// <inheritdoc /> /// <inheritdoc />
public void ResetColor() public int WindowWidth { get; set; } = 232; // Windows defaults
{
ForegroundColor = ConsoleColor.Gray; /// <inheritdoc />
BackgroundColor = ConsoleColor.Black; public int WindowHeight { get; set; } = 14; // Windows defaults
}
/// <inheritdoc /> /// <inheritdoc />
public int CursorLeft { get; set; } public int CursorLeft { get; set; }
@@ -77,7 +76,14 @@ public class FakeConsole : IConsole, IDisposable
/// Enqueues a simulated key press, which can then be read by calling <see cref="ReadKey"/>. /// Enqueues a simulated key press, which can then be read by calling <see cref="ReadKey"/>.
/// </summary> /// </summary>
public void EnqueueKey(ConsoleKeyInfo key) => _keys.Enqueue(key); public void EnqueueKey(ConsoleKeyInfo key) => _keys.Enqueue(key);
/// <inheritdoc />
public void ResetColor()
{
ForegroundColor = ConsoleColor.Gray;
BackgroundColor = ConsoleColor.Black;
}
/// <inheritdoc /> /// <inheritdoc />
public void Clear() public void Clear()
{ {
@@ -85,7 +91,7 @@ public class FakeConsole : IConsole, IDisposable
/// <inheritdoc /> /// <inheritdoc />
public CancellationToken RegisterCancellationHandler() => _cancellationTokenSource.Token; public CancellationToken RegisterCancellationHandler() => _cancellationTokenSource.Token;
/// <summary> /// <summary>
/// Sends a cancellation signal to the currently executing command. /// Sends a cancellation signal to the currently executing command.
/// </summary> /// </summary>

View File

@@ -15,7 +15,7 @@ public interface IConsole
ConsoleReader Input { get; } ConsoleReader Input { get; }
/// <summary> /// <summary>
/// Whether the input stream is redirected. /// Gets a value that indicates whether input has been redirected from the standard input stream.
/// </summary> /// </summary>
bool IsInputRedirected { get; } bool IsInputRedirected { get; }
@@ -25,7 +25,7 @@ public interface IConsole
ConsoleWriter Output { get; } ConsoleWriter Output { get; }
/// <summary> /// <summary>
/// Whether the output stream is redirected. /// Gets a value that indicates whether output has been redirected from the standard output stream.
/// </summary> /// </summary>
bool IsOutputRedirected { get; } bool IsOutputRedirected { get; }
@@ -35,32 +35,37 @@ public interface IConsole
ConsoleWriter Error { get; } ConsoleWriter Error { get; }
/// <summary> /// <summary>
/// Whether the error stream is redirected. /// Gets a value that indicates whether error output has been redirected from the standard error stream.
/// </summary> /// </summary>
bool IsErrorRedirected { get; } bool IsErrorRedirected { get; }
/// <summary> /// <summary>
/// Current foreground color. /// Gets or sets the foreground color of the console
/// </summary> /// </summary>
ConsoleColor ForegroundColor { get; set; } ConsoleColor ForegroundColor { get; set; }
/// <summary> /// <summary>
/// Current background color. /// Gets or sets the background color of the console.
/// </summary> /// </summary>
ConsoleColor BackgroundColor { get; set; } ConsoleColor BackgroundColor { get; set; }
/// <summary> /// <summary>
/// Resets foreground and background colors to their default values. /// Gets or sets the width of the console window.
/// </summary> /// </summary>
void ResetColor(); int WindowWidth { get; set; }
/// <summary> /// <summary>
/// Cursor left offset. /// Gets or sets the height of the console window.
/// </summary>
int WindowHeight { get; set; }
/// <summary>
/// Gets or sets the column position of the cursor within the buffer area.
/// </summary> /// </summary>
int CursorLeft { get; set; } int CursorLeft { get; set; }
/// <summary> /// <summary>
/// Cursor top offset. /// Gets or sets the row position of the cursor within the buffer area.
/// </summary> /// </summary>
int CursorTop { get; set; } int CursorTop { get; set; }
@@ -68,12 +73,17 @@ public interface IConsole
/// Obtains the next character or function key pressed by the user. /// Obtains the next character or function key pressed by the user.
/// </summary> /// </summary>
ConsoleKeyInfo ReadKey(bool intercept = false); ConsoleKeyInfo ReadKey(bool intercept = false);
/// <summary>
/// Sets the foreground and background console colors to their defaults.
/// </summary>
void ResetColor();
/// <summary> /// <summary>
/// Clears the console buffer and corresponding console window of display information. /// Clears the console buffer and corresponding console window of display information.
/// </summary> /// </summary>
void Clear(); void Clear();
/// <summary> /// <summary>
/// Registers a handler for the interrupt signal (Ctrl+C) on the console and returns /// Registers a handler for the interrupt signal (Ctrl+C) on the console and returns
/// a token representing the cancellation request. /// a token representing the cancellation request.

View File

@@ -41,7 +41,21 @@ public class SystemConsole : IConsole, IDisposable
get => Console.BackgroundColor; get => Console.BackgroundColor;
set => Console.BackgroundColor = value; set => Console.BackgroundColor = value;
} }
/// <inheritdoc />
public int WindowWidth
{
get => Console.WindowWidth;
set => Console.WindowWidth = value;
}
/// <inheritdoc />
public int WindowHeight
{
get => Console.WindowHeight;
set => Console.WindowHeight = value;
}
/// <inheritdoc /> /// <inheritdoc />
public int CursorLeft public int CursorLeft
{ {
@@ -66,15 +80,15 @@ public class SystemConsole : IConsole, IDisposable
Error = ConsoleWriter.Create(this, Console.OpenStandardError()); Error = ConsoleWriter.Create(this, Console.OpenStandardError());
} }
/// <inheritdoc />
public ConsoleKeyInfo ReadKey(bool intercept = false) => Console.ReadKey(intercept);
/// <inheritdoc /> /// <inheritdoc />
public void ResetColor() => Console.ResetColor(); public void ResetColor() => Console.ResetColor();
/// <inheritdoc />
public ConsoleKeyInfo ReadKey(bool intercept = false) => Console.ReadKey(intercept);
/// <inheritdoc /> /// <inheritdoc />
public void Clear() => Console.Clear(); public void Clear() => Console.Clear();
/// <inheritdoc /> /// <inheritdoc />
public CancellationToken RegisterCancellationHandler() public CancellationToken RegisterCancellationHandler()
{ {
@@ -95,7 +109,7 @@ public class SystemConsole : IConsole, IDisposable
return (_cancellationTokenSource = cts).Token; return (_cancellationTokenSource = cts).Token;
} }
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() public void Dispose()
{ {

View File

@@ -1,7 +1,7 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<Version>2.2.5</Version> <Version>2.3</Version>
<Company>Tyrrrz</Company> <Company>Tyrrrz</Company>
<Copyright>Copyright (C) Oleksii Holub</Copyright> <Copyright>Copyright (C) Oleksii Holub</Copyright>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>

View File

@@ -14,7 +14,7 @@
**CliFx** is a simple to use, yet powerful framework for building command line applications. **CliFx** is a simple to use, yet powerful framework for building command line applications.
Its primary goal is to completely take over the user input layer, allowing you to forget about infrastructural concerns and instead focus on writing your application. Its primary goal is to completely take over the user input layer, allowing you to forget about infrastructural concerns and instead focus on writing your application.
## Terms of use ## Terms of use<sup>[[?]](https://github.com/Tyrrrz/.github/blob/master/docs/why-so-political.md)</sup>
By using this project or its source code, for any purpose and in any shape or form, you grant your **implicit agreement** to all the following statements: By using this project or its source code, for any purpose and in any shape or form, you grant your **implicit agreement** to all the following statements:
@@ -65,10 +65,12 @@ public static class Program
} }
``` ```
> ⚠️ Ensure that your `Main()` method returns the integer exit code provided by `CliApplication.RunAsync()`, as shown in the above example. > **Warning**:
> Ensure that your `Main()` method returns the integer exit code provided by `CliApplication.RunAsync()`, as shown in the above example.
> Exit code is used to communicate execution result to the parent process, so it's important that your program propagates it. > Exit code is used to communicate execution result to the parent process, so it's important that your program propagates it.
> 💡 When calling `CliApplication.RunAsync()`, **CliFx** resolves command line arguments and environment variables from `Environment.GetCommandLineArgs()` and `Environment.GetEnvironmentVariables()` respectively. > **Note**:
> When calling `CliApplication.RunAsync()`, **CliFx** resolves command line arguments and environment variables from `Environment.GetCommandLineArgs()` and `Environment.GetEnvironmentVariables()` respectively.
> You can also provide them explicitly if you choose. > You can also provide them explicitly if you choose.
The code above uses `AddCommandsFromThisAssembly()` to detect command types defined within the current assembly. The code above uses `AddCommandsFromThisAssembly()` to detect command types defined within the current assembly.
@@ -159,7 +161,8 @@ public class LogCommand : ICommand
} }
``` ```
> 💡 **CliFx** has built-in analyzers that detect common errors in command definitions. > **Note**:
> **CliFx** has built-in analyzers that detect common errors in command definitions.
> Your code will not compile if the command contains duplicate options, overlapping parameters, or otherwise invalid configuration. > Your code will not compile if the command contains duplicate options, overlapping parameters, or otherwise invalid configuration.
In order to execute this command, at a minimum, the user needs to provide the input value: In order to execute this command, at a minimum, the user needs to provide the input value:
@@ -483,7 +486,8 @@ COMMANDS
You can run `dotnet myapp.dll cmd1 [command] --help` to show help on a specific command. You can run `dotnet myapp.dll cmd1 [command] --help` to show help on a specific command.
``` ```
> 💡 Defining a default (unnamed) command is not required. > **Note**:
> Defining a default (unnamed) command is not required.
> If it's absent, running the application without specifying a command will just show the root level help text. > If it's absent, running the application without specifying a command will just show the root level help text.
### Reporting errors ### Reporting errors
@@ -528,7 +532,8 @@ Division by zero is not supported.
133 133
``` ```
> ⚠️ Even though exit codes are represented by 32-bit integers in .NET, using values outside 8-bit unsigned range will cause an overflow on Unix systems. > **Warning**:
> Even though exit codes are represented by 32-bit integers in .NET, using values outside 8-bit unsigned range will cause an overflow on Unix systems.
> To avoid unexpected results, use numbers between 1 and 255 for exit codes that indicate failure. > To avoid unexpected results, use numbers between 1 and 255 for exit codes that indicate failure.
### Graceful cancellation ### Graceful cancellation
@@ -561,7 +566,8 @@ public class CancellableCommand : ICommand
} }
``` ```
> ⚠️ Forceful termination of a command can only be delayed once. > **Warning**:
> Forceful termination of a command can only be delayed once.
> If the user issues a second interrupt signal, the process will be killed immediately without waiting for graceful cancellation. > If the user issues a second interrupt signal, the process will be killed immediately without waiting for graceful cancellation.
### Type activation ### Type activation
@@ -591,7 +597,7 @@ public static class Program
return await new CliApplicationBuilder() return await new CliApplicationBuilder()
.AddCommandsFromThisAssembly() .AddCommandsFromThisAssembly()
.UseTypeActivator(serviceProvider.GetService) .UseTypeActivator(serviceProvider)
.Build() .Build()
.RunAsync(); .RunAsync();
} }