146 lines
4.9 KiB
C#

namespace Spectre.Console.Cli;
internal static class TemplateParser
{
public sealed class ArgumentResult
{
public string Value { get; set; }
public bool Required { get; set; }
public ArgumentResult(string value, bool required)
{
Value = value;
Required = required;
}
}
public sealed class OptionResult
{
public List<string> LongNames { get; set; }
public List<string> ShortNames { get; set; }
public string? Value { get; set; }
public bool ValueIsOptional { get; set; }
public OptionResult()
{
ShortNames = new List<string>();
LongNames = new List<string>();
}
}
public static ArgumentResult ParseArgumentTemplate(string template)
{
var valueName = default(string);
var required = false;
foreach (var token in TemplateTokenizer.Tokenize(template))
{
if (token.TokenKind == TemplateToken.Kind.ShortName ||
token.TokenKind == TemplateToken.Kind.LongName)
{
throw CommandTemplateException.ArgumentCannotContainOptions(template, token);
}
if (token.TokenKind == TemplateToken.Kind.OptionalValue ||
token.TokenKind == TemplateToken.Kind.RequiredValue)
{
if (!string.IsNullOrWhiteSpace(valueName))
{
throw CommandTemplateException.MultipleValuesAreNotSupported(template, token);
}
if (string.IsNullOrWhiteSpace(token.Value))
{
throw CommandTemplateException.ValuesMustHaveName(template, token);
}
valueName = token.Value;
required = token.TokenKind == TemplateToken.Kind.RequiredValue;
}
}
if (valueName == null)
{
throw CommandTemplateException.ArgumentsMustHaveValueName(template);
}
return new ArgumentResult(valueName, required);
}
public static OptionResult ParseOptionTemplate(string template)
{
var result = new OptionResult();
foreach (var token in TemplateTokenizer.Tokenize(template))
{
if (token.TokenKind == TemplateToken.Kind.LongName || token.TokenKind == TemplateToken.Kind.ShortName)
{
if (string.IsNullOrWhiteSpace(token.Value))
{
throw CommandTemplateException.OptionsMustHaveName(template, token);
}
if (char.IsDigit(token.Value[0]))
{
throw CommandTemplateException.OptionNamesCannotStartWithDigit(template, token);
}
foreach (var character in token.Value)
{
if (!char.IsLetterOrDigit(character) && character != '-' && character != '_')
{
throw CommandTemplateException.InvalidCharacterInOptionName(template, token, character);
}
}
}
if (token.TokenKind == TemplateToken.Kind.LongName)
{
if (token.Value.Length == 1)
{
throw CommandTemplateException.LongOptionMustHaveMoreThanOneCharacter(template, token);
}
result.LongNames.Add(token.Value);
}
if (token.TokenKind == TemplateToken.Kind.ShortName)
{
if (token.Value.Length > 1)
{
throw CommandTemplateException.ShortOptionMustOnlyBeOneCharacter(template, token);
}
result.ShortNames.Add(token.Value);
}
if (token.TokenKind == TemplateToken.Kind.RequiredValue ||
token.TokenKind == TemplateToken.Kind.OptionalValue)
{
if (!string.IsNullOrWhiteSpace(result.Value))
{
throw CommandTemplateException.MultipleOptionValuesAreNotSupported(template, token);
}
foreach (var character in token.Value)
{
if (!char.IsLetterOrDigit(character) &&
character != '=' && character != '-' && character != '_' && character != '|')
{
throw CommandTemplateException.InvalidCharacterInValueName(template, token, character);
}
}
result.Value = token.Value.ToUpperInvariant();
result.ValueIsOptional = token.TokenKind == TemplateToken.Kind.OptionalValue;
}
}
if (result.LongNames.Count == 0 &&
result.ShortNames.Count == 0)
{
throw CommandTemplateException.MissingLongAndShortName(template, null);
}
return result;
}
}