// 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;
        }
Пример #2
0
        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);
        }
Пример #3
0
        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)));
        }
Пример #4
0
        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);
        }
Пример #5
0
        /// <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));
            }
        }
Пример #6
0
        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);
        }
Пример #7
0
        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));
        }
Пример #8
0
        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));
            }
        }
Пример #9
0
        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));
            }
        }
Пример #10
0
        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));
            }
        }
Пример #11
0
        // 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...");
        }
Пример #12
0
        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);
        }
Пример #13
0
        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);
 }
Пример #15
0
 protected abstract IParsingResult DoApply(ICommandParserOptions options, CommandModel model, string[] args, ref int argIndex);
Пример #16
0
        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));
        }
Пример #17
0
 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);
 }
Пример #18
0
        public string GetSyntaxString(ICommandParserOptions options)
        {
            var wrapper = (this.IsMandatory) ? "<{0}{1}>" : "[{0}{1}]";

            return(string.Format(wrapper, this.GetInnerSyntaxSelector(options), this.GetInnerSyntaxString(options)));
        }
Пример #19
0
 protected object BuildModelForCommand(CommandInfo cmdInfo, IEnumerable <string> arguments, ICommandParserOptions options, ICommandOutput outputManager)
 {
     return(cmdInfo.CommandModelBuilder.BuildModel(arguments, options, outputManager));
 }
Пример #20
0
        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);
        }
Пример #21
0
        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));
 }