mirror of
https://github.com/Tyrrrz/CliFx.git
synced 2025-10-25 15:19:17 +00:00
221 lines
6.9 KiB
C#
221 lines
6.9 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using CliFx.Utils.Extensions;
|
|
|
|
namespace CliFx.Input;
|
|
|
|
internal partial class CommandInput(
|
|
string? commandName,
|
|
IReadOnlyList<DirectiveInput> directives,
|
|
IReadOnlyList<ParameterInput> parameters,
|
|
IReadOnlyList<OptionInput> options,
|
|
IReadOnlyList<EnvironmentVariableInput> environmentVariables
|
|
)
|
|
{
|
|
public string? CommandName { get; } = commandName;
|
|
|
|
public IReadOnlyList<DirectiveInput> Directives { get; } = directives;
|
|
|
|
public IReadOnlyList<ParameterInput> Parameters { get; } = parameters;
|
|
|
|
public IReadOnlyList<OptionInput> Options { get; } = options;
|
|
|
|
public IReadOnlyList<EnvironmentVariableInput> EnvironmentVariables { get; } =
|
|
environmentVariables;
|
|
|
|
public bool HasArguments =>
|
|
!string.IsNullOrWhiteSpace(CommandName)
|
|
|| Directives.Any()
|
|
|| Parameters.Any()
|
|
|| Options.Any();
|
|
|
|
public bool IsDebugDirectiveSpecified => Directives.Any(d => d.IsDebugDirective);
|
|
|
|
public bool IsPreviewDirectiveSpecified => Directives.Any(d => d.IsPreviewDirective);
|
|
}
|
|
|
|
internal partial class CommandInput
|
|
{
|
|
private static IReadOnlyList<DirectiveInput> ParseDirectives(
|
|
IReadOnlyList<string> commandLineArguments,
|
|
ref int index
|
|
)
|
|
{
|
|
var result = new List<DirectiveInput>();
|
|
|
|
// Consume all consecutive directive arguments
|
|
for (; index < commandLineArguments.Count; index++)
|
|
{
|
|
var argument = commandLineArguments[index];
|
|
|
|
// Break on the first non-directive argument
|
|
if (!argument.StartsWith('[') || !argument.EndsWith(']'))
|
|
break;
|
|
|
|
var directiveName = argument.Substring(1, argument.Length - 2);
|
|
result.Add(new DirectiveInput(directiveName));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private static string? ParseCommandName(
|
|
IReadOnlyList<string> commandLineArguments,
|
|
ISet<string> commandNames,
|
|
ref int index
|
|
)
|
|
{
|
|
var potentialCommandNameComponents = new List<string>();
|
|
var commandName = default(string?);
|
|
|
|
var lastIndex = index;
|
|
|
|
// Append arguments to a buffer until we find the longest sequence that represents
|
|
// a valid command name.
|
|
for (var i = index; i < commandLineArguments.Count; i++)
|
|
{
|
|
var argument = commandLineArguments[i];
|
|
|
|
potentialCommandNameComponents.Add(argument);
|
|
|
|
var potentialCommandName = potentialCommandNameComponents.JoinToString(" ");
|
|
if (commandNames.Contains(potentialCommandName))
|
|
{
|
|
// Record the position but continue the loop in case we find
|
|
// a longer (more specific) match.
|
|
commandName = potentialCommandName;
|
|
lastIndex = i;
|
|
}
|
|
}
|
|
|
|
// Move the index to the position where the command name ended
|
|
if (!string.IsNullOrWhiteSpace(commandName))
|
|
index = lastIndex + 1;
|
|
|
|
return commandName;
|
|
}
|
|
|
|
private static IReadOnlyList<ParameterInput> ParseParameters(
|
|
IReadOnlyList<string> commandLineArguments,
|
|
ref int index
|
|
)
|
|
{
|
|
var result = new List<ParameterInput>();
|
|
|
|
// Consume all arguments until the first option identifier
|
|
for (; index < commandLineArguments.Count; index++)
|
|
{
|
|
var argument = commandLineArguments[index];
|
|
|
|
var isOptionIdentifier =
|
|
// Name
|
|
argument.StartsWith("--", StringComparison.Ordinal)
|
|
&& argument.Length > 2
|
|
&& char.IsLetter(argument[2])
|
|
||
|
|
// Short name
|
|
argument.StartsWith('-')
|
|
&& argument.Length > 1
|
|
&& char.IsLetter(argument[1]);
|
|
|
|
// Break on the first option identifier
|
|
if (isOptionIdentifier)
|
|
break;
|
|
|
|
result.Add(new ParameterInput(argument));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private static IReadOnlyList<OptionInput> ParseOptions(
|
|
IReadOnlyList<string> commandLineArguments,
|
|
ref int index
|
|
)
|
|
{
|
|
var result = new List<OptionInput>();
|
|
|
|
var lastOptionIdentifier = default(string?);
|
|
var lastOptionValues = new List<string>();
|
|
|
|
// Consume and group all remaining arguments into options
|
|
for (; index < commandLineArguments.Count; index++)
|
|
{
|
|
var argument = commandLineArguments[index];
|
|
|
|
// Name
|
|
if (
|
|
argument.StartsWith("--", StringComparison.Ordinal)
|
|
&& argument.Length > 2
|
|
&& char.IsLetter(argument[2])
|
|
)
|
|
{
|
|
// Flush previous
|
|
if (!string.IsNullOrWhiteSpace(lastOptionIdentifier))
|
|
result.Add(new OptionInput(lastOptionIdentifier, lastOptionValues));
|
|
|
|
lastOptionIdentifier = argument[2..];
|
|
lastOptionValues = [];
|
|
}
|
|
// Short name
|
|
else if (argument.StartsWith('-') && argument.Length > 1 && char.IsLetter(argument[1]))
|
|
{
|
|
foreach (var identifier in argument[1..])
|
|
{
|
|
// Flush previous
|
|
if (!string.IsNullOrWhiteSpace(lastOptionIdentifier))
|
|
result.Add(new OptionInput(lastOptionIdentifier, lastOptionValues));
|
|
|
|
lastOptionIdentifier = identifier.AsString();
|
|
lastOptionValues = [];
|
|
}
|
|
}
|
|
// Value
|
|
else if (!string.IsNullOrWhiteSpace(lastOptionIdentifier))
|
|
{
|
|
lastOptionValues.Add(argument);
|
|
}
|
|
}
|
|
|
|
// Flush the last option
|
|
if (!string.IsNullOrWhiteSpace(lastOptionIdentifier))
|
|
result.Add(new OptionInput(lastOptionIdentifier, lastOptionValues));
|
|
|
|
return result;
|
|
}
|
|
|
|
public static CommandInput Parse(
|
|
IReadOnlyList<string> commandLineArguments,
|
|
IReadOnlyDictionary<string, string> environmentVariables,
|
|
IReadOnlyList<string> availableCommandNames
|
|
)
|
|
{
|
|
var index = 0;
|
|
|
|
var parsedDirectives = ParseDirectives(commandLineArguments, ref index);
|
|
|
|
var parsedCommandName = ParseCommandName(
|
|
commandLineArguments,
|
|
availableCommandNames.ToHashSet(StringComparer.OrdinalIgnoreCase),
|
|
ref index
|
|
);
|
|
|
|
var parsedParameters = ParseParameters(commandLineArguments, ref index);
|
|
|
|
var parsedOptions = ParseOptions(commandLineArguments, ref index);
|
|
|
|
var parsedEnvironmentVariables = environmentVariables
|
|
.Select(kvp => new EnvironmentVariableInput(kvp.Key, kvp.Value))
|
|
.ToArray();
|
|
|
|
return new CommandInput(
|
|
parsedCommandName,
|
|
parsedDirectives,
|
|
parsedParameters,
|
|
parsedOptions,
|
|
parsedEnvironmentVariables
|
|
);
|
|
}
|
|
}
|