Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System.CommandLine.Parsing;

namespace Arcturus.CommandLine.Abstractions;

/// <summary>
/// Encapsulates all command-line specific information about an individual execution.
/// </summary>
public class CommandLineContext
{
/// <summary>
/// Gets or sets the <see cref="IServiceProvider"/> that provides access to the application's service container.
/// </summary>
public required IServiceProvider Services { get; init; }

/// <summary>
/// Gets or sets the command-line arguments.
/// </summary>
public required string[] Args { get; init; }

/// <summary>
/// Gets or sets the <see cref="Parser"/> for parsing command-line input.
/// </summary>
public required Parser Parser { get; init; }

/// <summary>
/// Gets or sets the <see cref="CancellationToken"/> for the execution.
/// </summary>
public CancellationToken CancellationToken { get; init; }

/// <summary>
/// Gets or sets the root command instance.
/// </summary>
public required ICommand RootCommand { get; init; }

/// <summary>
/// Gets a key/value collection that can be used to share data within the scope of this execution.
/// </summary>
public IDictionary<object, object?> Items { get; } = new Dictionary<object, object?>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Arcturus.CommandLine.Abstractions;

/// <summary>
/// A function that can process a command-line execution.
/// </summary>
/// <param name="context">The <see cref="CommandLineContext"/> for the current execution.</param>
/// <returns>A task that represents the completion of command processing.</returns>
public delegate Task CommandLineDelegate(CommandLineContext context);
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace Arcturus.CommandLine.Abstractions;

/// <summary>
/// Defines middleware for the command-line execution pipeline.
/// </summary>
public interface ICommandLineMiddleware
{
/// <summary>
/// Request handling method.
/// </summary>
/// <param name="context">The <see cref="CommandLineContext"/> for the current execution.</param>
/// <param name="next">The delegate representing the remaining middleware in the pipeline.</param>
/// <returns>A <see cref="Task"/> that represents the execution of this middleware.</returns>
Task InvokeAsync(CommandLineContext context, CommandLineDelegate next);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ public class OptionAttribute(
string name
, string? description) : Attribute
{
public OptionAttribute(
string name
, string? description
, bool required)
: this(name, description)
{
Required = required;
}
public string Name => name;
public string? Description => description;
}
public bool? Required { get; }
}
108 changes: 108 additions & 0 deletions src/Arcturus.Extensions.CommandLine/CliConsole.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
using System.Text.RegularExpressions;

namespace Arcturus.CommandLine;

public static class CliConsole
{
private static readonly Dictionary<string, ConsoleColor> ColorMap = new(StringComparer.OrdinalIgnoreCase)
{
["black"] = ConsoleColor.Black,
["darkblue"] = ConsoleColor.DarkBlue,
["darkgreen"] = ConsoleColor.DarkGreen,
["darkcyan"] = ConsoleColor.DarkCyan,
["darkred"] = ConsoleColor.DarkRed,
["darkmagenta"] = ConsoleColor.DarkMagenta,
["darkyellow"] = ConsoleColor.DarkYellow,
["gray"] = ConsoleColor.Gray,
["darkgray"] = ConsoleColor.DarkGray,
["blue"] = ConsoleColor.Blue,
["green"] = ConsoleColor.Green,
["cyan"] = ConsoleColor.Cyan,
["red"] = ConsoleColor.Red,
["magenta"] = ConsoleColor.Magenta,
["yellow"] = ConsoleColor.Yellow,
["white"] = ConsoleColor.White,
};

public static void Write(TextWriter writer, string text)
{
WriteMarkup(writer, text);
}
public static void WriteLine(TextWriter writer, string text)
{
WriteMarkup(writer, text + Environment.NewLine);
}
public static void Write(string text)
{
WriteMarkup(Console.Out, text);
}
public static void WriteLine(string text)
{
WriteMarkup(Console.Out, text + Environment.NewLine);
}

private static void WriteMarkup(TextWriter writer, string text)
{
if (string.IsNullOrEmpty(text))
{
return;
}

// Check if we can apply colors - console must be available and not redirected
var canApplyColors = !Console.IsOutputRedirected && !Console.IsErrorRedirected;

if (!canApplyColors)
{
// For redirected output or non-console writers, strip markup tags and write plain text
var pattern = @"\[(\/?[^\]]+)\]";
var plainText = Regex.Replace(text, pattern, string.Empty);
writer.Write(plainText);
return;
}

var originalColor = Console.ForegroundColor;
var colorStack = new Stack<ConsoleColor>();

var pattern2 = @"\[(\/?[^\]]+)\]";
var matches = Regex.Matches(text, pattern2);
var lastIndex = 0;

foreach (Match match in matches)
{
// Write text before the tag
if (match.Index > lastIndex)
{
writer.Write(text.Substring(lastIndex, match.Index - lastIndex));
}

var tag = match.Groups[1].Value;

if (tag == "/")
{
// Reset to previous color or original
Console.ForegroundColor = colorStack.Count > 0 ? colorStack.Pop() : originalColor;
}
else if (ColorMap.TryGetValue(tag, out var color))
{
colorStack.Push(Console.ForegroundColor);
Console.ForegroundColor = color;
}
else
{
// Not a recognized tag, write it as-is
writer.Write(match.Value);
}

lastIndex = match.Index + match.Length;
}

// Write remaining text
if (lastIndex < text.Length)
{
writer.Write(text.Substring(lastIndex));
}

// Reset color at the end
Console.ForegroundColor = originalColor;
}
}
4 changes: 2 additions & 2 deletions src/Arcturus.Extensions.CommandLine/HostBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ internal static void AssignCommandLineBuilder(CommandLineBuilder commandLineBuil
_configureCommandLineBuilder(commandLineBuilder);
}

public static IHostBuilder ConfigureCommandLineBuilder(
this IHostBuilder builder
public static IHostApplicationBuilder ConfigureCommandLineBuilder(
this IHostApplicationBuilder builder
, Action<CommandLineBuilder> configureAction)
{
_configureCommandLineBuilder = configureAction;
Expand Down
Loading