// NO default public constructor - by design internal CommandEngine(CommandManager commandManager, ICommandParserOptions options, CommandEngineStyles styles, HelpPrinter printHelper, IConsole console) { if (commandManager == null) { throw new ArgumentNullException(nameof(commandManager)); } if (options == null) { throw new ArgumentNullException(nameof(options)); } if (styles == null) { throw new ArgumentNullException(nameof(styles)); } if (console == null) { throw new ArgumentNullException(nameof(console)); } this._commandManager = commandManager; this._console = console; this._options = options; this._styles = styles; this._helpPrinter = printHelper; }
public void Apply(ICommandParserOptions options, CommandModel model, string[] args, ref int argIndex) { if (model.GetType() != this.TargetProperty.DeclaringType) { throw new InvalidOperationException("Incompatible model type."); } this.DoApply(options, model, args, ref argIndex); }
public string GetSyntaxString(ICommandParserOptions options) { // TODO: take into consideration this: https://technet.microsoft.com/en-us/library/cc771080(v=ws.11).aspx (and perhaps Linux-equivalent...) // TODO: maybe separate syntax printing from syntax info record? var wrapper = (this.IsMandatory) ? "<{0}{1}>" : "[{0}{1}]"; return(string.Format(wrapper, this.GetInnerSyntaxSelector(options), this.GetInnerSyntaxString(options))); }
protected void ValidateCommandLine <TModel>(string cmd, Type commandType, ICommandParserOptions parserOptions, TModel expectedModel) where TModel : CommandModel { var validatingCommandLine = CommandLineUtilities.SplitCommandLine(cmd).ToArray();; var cmdManager = new CommandManager(new Type[] { commandType }, new DefaultCommandResolver()); var outputManager = new OutputManager(Console, CommandEngineStyles.DefaultStyles, CultureInfo.InvariantCulture); var cmdInfo = cmdManager.FindCommand(validatingCommandLine[0]); var model = this.BuildModelForCommand(cmdInfo, validatingCommandLine.Skip(1), parserOptions, outputManager); model.ShouldNotBeNull(); model.ShouldBeOfType(expectedModel.GetType()); model.ShouldBe(expectedModel); }
/// <inheritdoc /> protected override IParsingResult DoApply(ICommandParserOptions options, CommandModel model, string[] args, ref int argIndex) { try { this.TargetProperty.SetValue(model, true); argIndex++; return(ParsingSuccess.Instance); } catch (Exception e) { return(new ParsingFailure(e.Message)); } }
public CommandModel BuildModel(IEnumerable <string> arguments, ICommandParserOptions options) { var model = Activator.CreateInstance(this._modelType) as CommandModel; string[] args = arguments.ToArray(); int argIndex = 0; int unnamedIndex = 0; string[] flagSwitchPrefixes = options.SwitchCharacters.Concat(options.FlagCharacters).Distinct().ToArray(); while (argIndex < args.Length) { string arg = args[argIndex].Trim(); // need to skip unnamed parameters there are just exactly like one of prefixes (i.e. - single slash or backslash) if (flagSwitchPrefixes.All(p => p != arg) && flagSwitchPrefixes.Any(p => arg.StartsWith(p))) { var propertyParser = this.FindProperty(options, arg); if (propertyParser == null) { // TODO: write error about invalid switch return(null); } propertyParser.Apply(options, model, args, ref argIndex); } else { var propertyParser = this._switchlessParsers.SingleOrDefault(p => p.ArgumentIndex == unnamedIndex++); if (propertyParser == null) { // TODO: write error about too many switch-less arguments return(null); } propertyParser.Apply(options, model, args, ref argIndex); } argIndex++; } // TODO: validate that all mandatory options were provided and switch-less arguments too // TODO: validate mixed / non mixed mode for switch-less parameters return(model); }
protected override IParsingResult DoApply(ICommandParserOptions options, CommandModel model, string[] args, ref int argIndex) { // read more arguments if necessary if (options.OptionArgumentMode == CommandOptionArgumentMode.Separated) { string[] optionArguments = new string[this._expectedArguments]; argIndex++; // skip option arg itself for (int i = 0; i < optionArguments.Length; ++i) { optionArguments[i] = this.SafelyGetNextArg(args, ref argIndex); } return(this.DoApplySwitch(model, optionArguments, options)); } else if (options.OptionArgumentMode == CommandOptionArgumentMode.Joined) { string firstArg = this.SafelyGetNextArg(args, ref argIndex); string[] parts = firstArg.Split(options.OptionArgumentJoinCharacater); if (parts.Length < 2) { return(new ParsingFailure($"Expected argument syntax is '{parts[0]}{options.OptionArgumentJoinCharacater}<some_value>'.")); } return(this.DoApplySwitch(model, parts.Skip(1).ToArray(), options)); // will support multi-values as well... } else if (options.OptionArgumentMode == CommandOptionArgumentMode.Merged) { if (this._expectedArguments != 1) { throw new BadImplementationException( "Options in Merged mode can only have one value. This mode could not support more.", this.GetType()); } string firstArg = this.SafelyGetNextArg(args, ref argIndex); string[] combinations = CommandsSyntaxHelpers.Combine(options.SwitchCharacters, this._switchLetters, (s, s1) => s + s1).ToArray(); string remainder = firstArg.CutLeftFirst(combinations); return(this.DoApplySwitch(model, new[] { remainder }, options)); } throw new ArgumentOutOfRangeException(nameof(options), nameof(options.OptionArgumentMode)); }
private string GetSwitchSyntax(ICommandParserOptions options) { string literal = this.Literals.First(); string value = string.Join("|", this.SwitchValues); switch (options.OptionArgumentMode) { case CommandOptionArgumentMode.Separated: return($"{literal} {value}"); case CommandOptionArgumentMode.Merged: return($"{literal}{value}"); case CommandOptionArgumentMode.Joined: return($"{literal}{options.OptionArgumentJoinCharacater}{value}"); default: throw new ArgumentOutOfRangeException(nameof(CommandOptionArgumentMode)); } }
private string GetInnerSyntaxString(ICommandParserOptions options) { switch (this.OptionType) { case SyntaxOptionType.Flag: return(this.Literals.First()); case SyntaxOptionType.Switch: return(this.GetSwitchSyntax(options)); case SyntaxOptionType.CustomValueSwitch: return(this.GetCustomSwitchSyntax(options)); case SyntaxOptionType.Switchless: return($"\"{this.OptionName}\""); default: throw new ArgumentOutOfRangeException(nameof(SyntaxOptionType)); } }
private object GetInnerSyntaxSelector(ICommandParserOptions options) { switch (this.OptionType) { case SyntaxOptionType.Flag: return(options.FlagCharacters.First()); case SyntaxOptionType.Switch: return(options.SwitchCharacters.First()); case SyntaxOptionType.CustomValueSwitch: return(options.SwitchCharacters.First()); case SyntaxOptionType.Switchless: return(""); default: throw new ArgumentOutOfRangeException(nameof(SyntaxOptionType)); } }
// NO default public constructor - by design -> use builder internal CommandEngine(CommandManager commandManager, ICommandParserOptions options, CommandEngineStyles styles, HelpPrinter printHelper, IConsole console, IClipboard clipboard) { commandManager.Requires(nameof(commandManager)).IsNotNull(); options.Requires(nameof(options)).IsNotNull(); styles.Requires(nameof(styles)).IsNotNull(); printHelper.Requires(nameof(printHelper)).IsNotNull(); console.Requires(nameof(console)).IsNotNull(); clipboard.Requires(nameof(clipboard)).IsNotNull(); this._commandManager = commandManager; this._autoCompletionManager = new CommandAutoCompletionProvider(this._commandManager); this._virtualLine = new VirtualEntryLine(console, clipboard, styles.Default); this._console = console; this._options = options; this._styles = styles; this._helpPrinter = printHelper; this._outputManager = new OutputManager(this._console, this._styles, this._options.UiCulture); // TODO: check option before displaying this message // this._console.WriteLine(styles.Default, "Initializing ObscureWare's command engine..."); }
private BasePropertyParser FindProperty(ICommandParserOptions options, string argSyntax) { // Flags if (!options.AllowFlagsAsOneArgument) { foreach (var flagPrefix in options.FlagCharacters) { string cleanFlag = argSyntax.CutLeftFirst(flagPrefix); FlagPropertyParser parser; if (this._flagParsers.TryGetValue(cleanFlag, out parser)) { return(parser); } } } else { if (this._flagParsers.Keys.Any(flag => flag.Length > 1)) { throw new BadImplementationException($"Cannot use {nameof(options.AllowFlagsAsOneArgument)} mode with flags longer than one character.", this._modelType); } // TODO: implement, with above exception this will be easier... // TODO: add multi-flag parser here throw new NotImplementedException(); } // Switches foreach (var switchPrefix in options.SwitchCharacters) { string cleanFlag = argSyntax.CutLeftFirst(switchPrefix); BaseSwitchPropertyParser parser = this._switchParsers.FirstOrDefault(p => p.Key.Equals(cleanFlag)).Value; return(parser); } return(null); }
public HelpPrinter(ICommandParserOptions options, IHelpStyles helpStyles, IConsole console) { if (options == null) { throw new ArgumentNullException(nameof(options)); } if (helpStyles == null) { throw new ArgumentNullException(nameof(helpStyles)); } if (console == null) { throw new ArgumentNullException(nameof(console)); } this._options = options; this._helpStyles = helpStyles; this._console = console; this._allInlineHelpOptions = CommandsSyntaxHelpers.Combine(this._options.FlagCharacters, HelpCommands, ((s, s1) => s + s1)).ToArray(); this._commandNameComparer = (options.CommandsSensitivenes == CommandCaseSensitivenes.Sensitive) ? SensitiveComparer : InsensitiveComparer; }
/// <inheritdoc /> protected override void DoApply(ICommandParserOptions options, CommandModel model, string[] args, ref int argIndex) { this.TargetProperty.SetValue(model, true); }
protected abstract IParsingResult DoApply(ICommandParserOptions options, CommandModel model, string[] args, ref int argIndex);
public IParsingResult Apply(ICommandParserOptions options, CommandModel model, string[] args, ref int argIndex) { model.Requires(nameof(model)).IsNotNull().Evaluate(t => this.TargetProperty.DeclaringType.IsAssignableFrom(t.GetType()), @"Incompatible model type."); return(this.DoApply(options, model, args, ref argIndex)); }
public void various_combination_of_models_shall_be_parsed_properly_no_matter_what_are_parsing_syntax_settings(Type commandType, ICommandParserOptions parserOptions, string commandText, object expectedModel) { this.ValidateCommandLine(commandText, commandType, parserOptions, expectedModel as CommandModel); }
public string GetSyntaxString(ICommandParserOptions options) { var wrapper = (this.IsMandatory) ? "<{0}{1}>" : "[{0}{1}]"; return(string.Format(wrapper, this.GetInnerSyntaxSelector(options), this.GetInnerSyntaxString(options))); }
protected object BuildModelForCommand(CommandInfo cmdInfo, IEnumerable <string> arguments, ICommandParserOptions options, ICommandOutput outputManager) { return(cmdInfo.CommandModelBuilder.BuildModel(arguments, options, outputManager)); }
private BasePropertyParser FindProperty(ICommandParserOptions options, string argSyntax) { // Flags if (!options.AllowFlagsAsOneArgument) { foreach (var flagPrefix in options.FlagCharacters) { string cleanFlag = argSyntax.CutLeftFirst(flagPrefix); FlagPropertyParser parser; if (this._flagParsers.TryGetValue(cleanFlag, out parser)) { return(parser); } } } else { if (this._flagParsers.Keys.Any(flag => flag.Length > 1)) { throw new BadImplementationException($"Cannot use {nameof(options.AllowFlagsAsOneArgument)} mode with flags longer than one character.", this._modelType); } // TODO: implement, with above exception this will be easier... // TODO: add multi-flag parser here throw new NotImplementedException(); } // Switches foreach (var switchPrefix in options.SwitchCharacters) { switch (options.OptionArgumentMode) { case CommandOptionArgumentMode.Separated: { string cleanFlag = argSyntax.CutLeftFirst(switchPrefix); var parser = this._switchParsers.FirstOrDefault(p => p.Key.Equals(cleanFlag)).Value; if (parser != null) { return(parser); } break; } case CommandOptionArgumentMode.Merged: { var availableSwitches = this._switchParsers.Keys.OrderByDescending(k => k.Length); string cleanFlag = argSyntax.CutLeftFirst(switchPrefix); string matchingKey = availableSwitches.FirstOrDefault(sw => cleanFlag.StartsWith(sw)); if (matchingKey != null) { return(this._switchParsers[matchingKey]); } break; } case CommandOptionArgumentMode.Joined: { string[] parts = argSyntax.Split(options.OptionArgumentJoinCharacater); string cleanFlag = parts[0].CutLeftFirst(switchPrefix); var parser = this._switchParsers.FirstOrDefault(p => p.Key.Equals(cleanFlag)).Value; if (parser != null) { return(parser); } break; } default: throw new ArgumentOutOfRangeException(nameof(options), nameof(options.OptionArgumentMode)); } } return(null); }
public CommandModel BuildModel(IEnumerable <string> arguments, ICommandParserOptions options, ICommandOutput output) { var model = Activator.CreateInstance(this._modelType) as CommandModel; string[] args = arguments.ToArray(); int argIndex = 0; int unnamedIndex = 0; string[] flagSwitchPrefixes = options.SwitchCharacters.Concat(options.FlagCharacters).Distinct().ToArray(); HashSet <string> usedProperties = new HashSet <string>(); this.ApplyDefaults(model); while (argIndex < args.Length) { string arg = args[argIndex].Trim(); IParsingResult result; // need to skip unnamed parameters there are just exactly like one of prefixes (i.e. - single slash or backslash) if (flagSwitchPrefixes.All(p => p != arg) && flagSwitchPrefixes.Any(p => arg.StartsWith(p))) { var propertyParser = this.FindProperty(options, arg); if (propertyParser == null) { output.PrintWarning($"Command's argument is not valid => \"{arg}\"."); return(null); } if (usedProperties.Contains(propertyParser.TargetProperty.Name)) { output.PrintWarning($"Similar argument has been already specified => \"{arg}\"."); return(null); } usedProperties.Add(propertyParser.TargetProperty.Name); result = propertyParser.Apply(options, model, args, ref argIndex); } else { var propertyParser = this._switchlessParsers.SingleOrDefault(p => p.ArgumentIndex == unnamedIndex++); if (propertyParser == null) { output.PrintWarning($"This command does not expect so many standalone arguments => \"{arg}\"."); return(null); } usedProperties.Add(propertyParser.TargetProperty.Name); result = propertyParser.Apply(options, model, args, ref argIndex); } if (!result.IsFine) { output.PrintWarning($"Parsing error => {result.Message}"); return(null); } //argIndex++; not needed, all parsers move index forward accordingly to parsing settings and expected number of arguments } foreach (var syntaxInfo in this._syntax) { if (syntaxInfo.IsMandatory && !usedProperties.Contains(syntaxInfo.TargetPropertyName)) { output.PrintWarning($"Expected mandatory argument has not been provided => \"{syntaxInfo.OptionName}\"."); return(null); } } // TODO: validate that all mandatory options were provided and switch-less arguments too // TODO: validate mixed / non mixed mode for switch-less parameters return(model); }
/// <inheritdoc /> protected override void DoApply(ICommandParserOptions options, CommandModel model, string[] args, ref int argIndex) { this.TargetProperty.SetValue(model, this._converter.TryConvert(args[argIndex], options.UiCulture)); }