private static (OptionRef?, IEnumerable <string>) ParseShortOptionRef(IReadOnlyList <string> input) { var match = _shortNamePattern.Match(input[0]); if (!match.Success) { return(null, input); } var optionName = OptionName.Parse(match.Groups["name"].Value); if (optionName == null) { // TODO: Create LogicErrorException throw new Exception("LOGIC ERROR: option name in option ref regex is not a valid option name"); } if (match.Groups["next"].Success) { var next = match.Groups["next"].Value; var optionRef = new OptionRef(optionName, next, JoinType.Adjoined); return(optionRef, input.Skip(1)); } if (input.Count > 1) { var next = input[1]; var optionRef = new OptionRef(optionName, next, JoinType.Space); return(optionRef, input.Skip(2)); } return(new OptionRef(optionName, null), input.Skip(1)); }
public (IEnumerable <Action <TOptions> >, IEnumerable <string>) Parse(IEnumerable <string> input) { var tokens = input?.ToList() ?? throw new ArgumentNullException(nameof(input)); var assignments = new List <Action <TOptions> >(); var arguments = new List <string>(); while (tokens.Count > 0) { // TODO: Encapsulate context and refactor (var optionRef, var newInput) = OptionRef.Parse(tokens); if (optionRef == null) { (tokens, arguments) = ConsumeArguments(tokens, arguments); continue; } var optionName = optionRef.OptionName; var option = optionName switch { OptionName.Short s => _options.FirstOrDefault(o => o.ShortName == s), OptionName.Long l => _options.FirstOrDefault(o => o.LongName == l), OptionName n => throw new LogicErrorException($"unknown {nameof(OptionName)} subtype '{n.GetType().Name}'"), }; // TODO: Treat these as arguments? Probably just have to put them after "--" // TODO: Better error communication. if (option == null) { throw new Exception($"Unknown option '{optionName}'"); } // TODO: Express this better :/ // An explicit value can only be assigned to an option with long-name/equals, default is "true" var value = option.IsFlag && optionRef.Join != OptionRef.JoinType.Equals ? "true" : optionRef.NextToken; // TODO: Better error communication. if (value == null) { throw new Exception($"Missing required argument for option '{optionName}'"); } assignments.Add(option.Parse(value)); tokens = newInput.ToList(); // Flags can only have values joined by '='. If a flag has a next token that wasn't joined by '=' then // the token needs to go back into the input. if (optionRef.NextToken != null && option.IsFlag && optionRef.Join != OptionRef.JoinType.Equals) { // If the token was adjoined then the value is a sequence of short options and needs a leading '-'. var token = optionRef.Join == OptionRef.JoinType.Adjoined ? $"-{optionRef.NextToken}" : optionRef.NextToken; tokens.Insert(0, token); } } return(assignments, arguments); }
private OptionRef(OptionName optionName, string?nextToken, JoinType join = default) { OptionName = optionName ?? throw new ArgumentNullException(nameof(optionName)); NextToken = nextToken; Join = join; }