Example #1
0
        private string Usage(Type type)
        {
            VerbAttribute verbAttr    = type.GetCustomAttributes <VerbAttribute>().FirstOrDefault();
            var           options     = OptionsClassProcessor.GetOptions(type);
            var           values      = OptionsClassProcessor.GetValues(type);
            var           restOfInput = OptionsClassProcessor.GetRestOfInputProperty(type);

            StringBuilder result = new StringBuilder();

            if (verbAttr != null)
            {
                result.AppendLine(verbAttr.HelpText.SplitToLines(LineWidth + 20, 0) + Environment.NewLine);
            }
            foreach (var option in options.Values)
            {
                result.AppendFormat("{0,-20} {1}" + Environment.NewLine,
                                    (option.IsShortOption ? "-" : "--") + option.Option,
                                    option.Description.SplitToLines(LineWidth, PrepadSpaces));
            }
            foreach (var value in values.Values.OrderBy(v => v.Index))
            {
                result.AppendFormat("#{0,-19} {1}" + Environment.NewLine,
                                    value.Index,
                                    value.Description.SplitToLines(LineWidth, PrepadSpaces));
            }
            if (restOfInput != null)
            {
                result.AppendFormat("{0,-20} {1}" + Environment.NewLine,
                                    "rest of input",
                                    restOfInput.Item2.Description.SplitToLines(LineWidth, PrepadSpaces));
            }
            return(result.ToString());
        }
Example #2
0
        private static ParseResult <object> SetRestOfInputPropertyIfAvailable(Type type, object instance, Tokenizer tokenizer)
        {
            var restOfInputProp = OptionsClassProcessor.GetRestOfInputProperty(type);

            if (restOfInputProp != null)
            {
                if (restOfInputProp.Item2.Required && tokenizer.AtEnd)
                {
                    return(ParseResult <object> .WithError("A value for the property '" + restOfInputProp.Item1.Name + "' is missing"));
                }
                restOfInputProp.Item1.SetValue(instance, tokenizer.RestOfInput);
            }
            return(null); // No error
        }
Example #3
0
        public ParseResult <object> Parse(IEnumerable <Type> verbTypes, string input)
        {
            Dictionary <string, Type> verbs = OptionsClassProcessor.GetVerbs(verbTypes);

            var tokenizer = new Tokenizer(input);
            var token     = tokenizer.NextToken;

            if (token.Kind != TokenKind.Value)
            {
                DisplayHelp(verbs, null);
                return(ParseResult <object> .WithError("Could not find an expected verb"));
            }

            if (token.Value == "help")
            {
                DisplayHelp(verbs, tokenizer);
                // The user intentionally asked for help, and this is not an error. The caller has to
                // be aware of the option that they are getting a null value in that case.
                return(ParseResult <object> .WithValue(null));
            }

            Type type;

            if (!verbs.TryGetValue(token.Value, out type))
            {
                DisplayHelp(verbs, null);
                return(ParseResult <object> .WithError("Could not find a match for verb '" + token.Value + "'"));
            }

            var result = Parse(type, tokenizer.RestOfInput);

            if (!result.Success)
            {
                _helpWriter.Write(Usage(type));
            }
            return(result);
        }
Example #4
0
        private ParseResult <object> Parse(Type type, string input)
        {
            // TODO Refactor this whole thing to use exception handling and store common objects in a class

            object instance = Activator.CreateInstance(type);

            Dictionary <string, OptionAttribute> options = OptionsClassProcessor.GetOptions(type);
            Dictionary <int, ValueAttribute>     values  = OptionsClassProcessor.GetValues(type);
            int             unsatisfiedCount             = options.Count + values.Count;
            int             nextValueToFill      = 0;
            OptionAttribute optionExpectingValue = null;
            Tokenizer       tokenizer            = new Tokenizer(input);

            while (!tokenizer.AtEnd && unsatisfiedCount > 0)
            {
                Token token = tokenizer.NextToken;
                switch (token.Kind)
                {
                case TokenKind.LongOption:
                case TokenKind.ShortOption:
                    if (optionExpectingValue != null)
                    {
                        return(ParseResult <object> .WithError("No value provided for option '" + optionExpectingValue.Option + "'"));
                    }
                    OptionAttribute optionAttr;
                    if (!options.TryGetValue(token.Value, out optionAttr))
                    {
                        return(ParseResult <object> .WithError("Unexpected option '" + token.Value + "'"));
                    }
                    if (optionAttr.Satisfied)
                    {
                        return(ParseResult <object> .WithError("Multiple values for option '" + token.Value + "' are not allowed"));
                    }

                    var alreadySatisfiedExclusiveOption = FirstAlreadySatisfiedMutuallyExclusiveOption(options, optionAttr);
                    if (alreadySatisfiedExclusiveOption != null)
                    {
                        return(ParseResult <object> .WithError("The option '" + token.Value + "' is mutually exclusive with the option '" + alreadySatisfiedExclusiveOption.Option + "' that was already satisfied"));
                    }

                    if (optionAttr.TargetProperty.PropertyType == typeof(bool))
                    {
                        optionAttr.TargetProperty.SetValue(instance, true);
                        optionAttr.Satisfied = true;
                        --unsatisfiedCount;
                    }
                    else
                    {
                        optionExpectingValue = optionAttr;
                    }
                    break;

                case TokenKind.Value:
                    if (optionExpectingValue != null)
                    {
                        string error = SetPropertyValue(optionExpectingValue, instance, token.Value);
                        if (error != null)
                        {
                            return(ParseResult <object> .WithError("Error setting option '" + optionExpectingValue.Option + "': " + error));
                        }
                        optionExpectingValue.Satisfied = true;
                        optionExpectingValue           = null;
                        --unsatisfiedCount;
                    }
                    else
                    {
                        if (nextValueToFill >= values.Count)
                        {
                            // There are no more values to fill, just options
                            return(ParseResult <object> .WithError("Unexpected plain value '" + token.Value + "'"));
                        }

                        ValueAttribute valueAttr = values[nextValueToFill];
                        string         error     = SetPropertyValue(valueAttr, instance, token.Value);
                        if (error != null)
                        {
                            return(ParseResult <object> .WithError("Error setting value #" + nextValueToFill + ": " + error));
                        }
                        valueAttr.Satisfied = true;
                        --unsatisfiedCount;
                        ++nextValueToFill;
                    }
                    break;

                case TokenKind.Error:
                    return(ParseResult <object> .WithError("Error parsing options: " + token.Value));
                }
            }

            SetDefaultValues(instance, options, values);

            var errorResult = FindUnsatisfiedOptionsAndValues(options, values);

            if (errorResult != null)
            {
                return(errorResult);
            }

            errorResult = SetRestOfInputPropertyIfAvailable(type, instance, tokenizer);
            if (errorResult != null)
            {
                return(errorResult);
            }

            return(ParseResult <object> .WithValue(instance));
        }