public static bool TryProcess(CommandInputSpecification spec, TParseResult parseResult, out DefaultCommandInput <TParseResult> result, out IReadOnlyList <CommandInputProcessingIssue> processingIssues) { List <CommandInputProcessingIssue> issues = null; List <InputElement> commandNameElements = null; foreach (IReadOnlyList <string> commandName in spec.CommandName) { if (TryProcessCommandName(commandName, parseResult, out List <InputElement> nameElements, out issues)) { commandNameElements = nameElements; break; } } if (commandNameElements is null) { result = null; processingIssues = issues; return(false); } List <InputElement> arguments = new List <InputElement>(); Dictionary <InputElement, InputElement> options = new Dictionary <InputElement, InputElement>(); InputElement currentOption = null; CommandOptionSpecification currentOptionSpec = null; InputElement selectedElement = null; for (int i = spec.CommandName.Count; i < parseResult.Sections.Count; ++i) { //If we're not looking at an option name if (!parseResult.Sections[i].StartsWith(spec.OptionPreamble) || parseResult.IsQuotedSection(i)) { if (currentOption is null) { InputElement currentElement = new InputElement(CommandInputLocation.Argument, parseResult.Sections[i], parseResult.Sections[i], i); if (i == parseResult.SelectedSection) { selectedElement = currentElement; } arguments.Add(currentElement); } else { //If the option isn't a defined one or it is and indicates that it accepts a value, add the section as an option value, // otherwise add it as an argument if (currentOptionSpec?.AcceptsValue ?? true) { InputElement currentElement = new InputElement(currentOption, CommandInputLocation.OptionValue, parseResult.Sections[i], parseResult.Sections[i], i); if (i == parseResult.SelectedSection) { selectedElement = currentElement; } options[currentOption] = currentElement; currentOption = null; currentOptionSpec = null; } else { InputElement currentElement = new InputElement(CommandInputLocation.Argument, parseResult.Sections[i], parseResult.Sections[i], i); if (i == parseResult.SelectedSection) { selectedElement = currentElement; } arguments.Add(currentElement); } } } //If we are looking at an option name else { //Otherwise, check to see whether the previous option had a required argument before committing it if (!(currentOption is null)) { options[currentOption] = null; if (currentOptionSpec?.RequiresValue ?? false) { issues.Add(new CommandInputProcessingIssue(CommandInputProcessingIssueKind.MissingRequiredOptionInput, currentOption.Text)); } } CommandOptionSpecification optionSpec = spec.Options.FirstOrDefault(x => x.Forms.Any(y => string.Equals(y, parseResult.Sections[i], StringComparison.Ordinal))); if (optionSpec is null) { issues.Add(new CommandInputProcessingIssue(CommandInputProcessingIssueKind.UnknownOption, parseResult.Sections[i])); } currentOption = new InputElement(CommandInputLocation.OptionName, parseResult.Sections[i], optionSpec?.Id, i); if (i == parseResult.SelectedSection) { selectedElement = currentOption; } currentOptionSpec = optionSpec; } } //Clear any option in progress if (!(currentOption is null)) { options[currentOption] = null; if (currentOptionSpec?.RequiresValue ?? false) { issues.Add(new CommandInputProcessingIssue(CommandInputProcessingIssueKind.MissingRequiredOptionInput, currentOption.Text)); } } //Check to make sure our argument count is in range, if not add an issue if (arguments.Count > spec.MaximumArguments || arguments.Count < spec.MinimumArguments) { issues.Add(new CommandInputProcessingIssue(CommandInputProcessingIssueKind.ArgumentCountOutOfRange, arguments.Count.ToString())); } //Build up the dictionary of options by normal form, then validate counts for every option in the spec Dictionary <string, IReadOnlyList <InputElement> > optionsByNormalForm = new Dictionary <string, IReadOnlyList <InputElement> >(StringComparer.Ordinal); foreach (KeyValuePair <InputElement, InputElement> entry in options) { if (entry.Key.NormalizedText is null) { continue; } if (!optionsByNormalForm.TryGetValue(entry.Key.NormalizedText, out IReadOnlyList <InputElement> rawBucket)) { optionsByNormalForm[entry.Key.NormalizedText] = rawBucket = new List <InputElement>(); } List <InputElement> bucket = (List <InputElement>)rawBucket; bucket.Add(entry.Value); } foreach (CommandOptionSpecification optionSpec in spec.Options) { if (!optionsByNormalForm.TryGetValue(optionSpec.Id, out IReadOnlyList <InputElement> values)) { optionsByNormalForm[optionSpec.Id] = values = new List <InputElement>(); } if (values.Count < optionSpec.MinimumOccurrences || values.Count > optionSpec.MaximumOccurrences) { issues.Add(new CommandInputProcessingIssue(CommandInputProcessingIssueKind.OptionUseCountOutOfRange, values.Count.ToString())); } } result = new DefaultCommandInput <TParseResult>(commandNameElements, arguments, optionsByNormalForm, selectedElement); processingIssues = issues; return(issues.Count == 0); }
public DefaultCommandInput(IReadOnlyList <InputElement> commandName, IReadOnlyList <InputElement> arguments, IReadOnlyDictionary <string, IReadOnlyList <InputElement> > options, InputElement selectedElement) { CommandName = commandName; Arguments = arguments; Options = options; SelectedElement = selectedElement; }