/// <summary> /// Generate the usage string based on the given type's attributes. /// <para>Optional positional parameters will be shown in square brackets.</para> /// <para>Required options will have an asterix prefix.</para> /// </summary> /// <param name="executableName"></param> /// <param name="indentationSpaces"></param> /// <returns></returns> public string Usage(string executableName = null, int indentationSpaces = 4) { string executable = Environment.GetCommandLineArgs()[0]; executableName = executableName ?? executable; string indentation = string.Join("", new int[indentationSpaces].Select(s => " ")); StringBuilder usage = new StringBuilder(); usage.AppendFormat("Usage: {0} [options] ", executableName); IOrderedEnumerable <MemberInfo> positionalParams = Utils.GetParameterMembers <T, PositionalParameterAttribute>(bindingFlags) .OrderBy(member => member.GetCustomAttribute <PositionalParameterAttribute>().Index); IOrderedEnumerable <MemberInfo> optionParams = Utils.GetParameterMembers <T, ParameterAttribute>(bindingFlags) .OrderBy(member => member.GetCustomAttribute <ParameterAttribute>().Names[0]); IOrderedEnumerable <MemberInfo> commands = Utils.GetParameterMembers <T, CommandAttribute>(bindingFlags) .OrderBy(member => member.GetCustomAttribute <CommandAttribute>().Names[0]); foreach (MemberInfo member in positionalParams) { // Print example line PositionalParameterAttribute param = member.GetCustomAttribute <PositionalParameterAttribute>(); if (ParamRequired <T>(defaultObject, param, member)) { usage.AppendFormat("<{0}> ", param.Name); } else { usage.AppendFormat("[{0}] ", param.Name); } } if (commands.Count() > 0) { usage.Append("<command>"); } usage.AppendLine(); foreach (MemberInfo member in positionalParams) { PositionalParameterAttribute param = member.GetCustomAttribute <PositionalParameterAttribute>(); // Print positional argument descriptions if (param.Description != null) { usage.AppendFormat("{0}{1}{2}{3}\n", indentation, param.Name, indentation, param.Description); } } if (commands.Count() > 0) { usage.AppendLine("Commands:"); foreach (MemberInfo member in commands) { CommandAttribute param = member.GetCustomAttribute <CommandAttribute>(); // Print command descriptions if (param.Description != null) { usage.AppendFormat("{0}{1}{2}{3}\n", indentation, param.Names[0], indentation, param.Description); } else { usage.AppendFormat("{0}{1}\n", indentation, param.Names[0]); } } } usage.AppendLine("Options:"); foreach (MemberInfo member in optionParams) { ParameterAttribute param = member.GetCustomAttribute <ParameterAttribute>(); object defaultValue = GetDefaultValue <T>(defaultObject, member); bool required = ParamRequired <T>(defaultObject, param, member); usage.Append(indentation).Append(required ? "* " : " "); for (int i = 0; i < param.Names.Length; i++) { if (i != 0) { usage.Append(", "); } usage.Append(param.Names[i]); } usage.AppendLine(); if (param.Description != null) { usage.AppendFormat("{0}{1}{2}\n", indentation, indentation, param.Description); } if (!required && defaultValue != null) { usage.AppendFormat("{0}{1}Default: {2}\n", indentation, indentation, defaultValue); } } return(usage.ToString()); }
/// <summary> /// Parse the arguments added to this object and serialize them into an existing instance of a T object. /// </summary> /// <param name="obj"></param> /// <param name="separators"></param> /// <returns></returns> public T Parse(T obj) { /* * Parse arguments */ RawArguments <T> rawArgs = new RawArguments <T>(bindingFlags) .Parse(args, separators); /* * Set named arguments */ foreach (MemberInfo member in Utils.GetParameterMembers <T, ParameterAttribute>(bindingFlags)) { ParameterAttribute param = member.GetCustomAttribute <ParameterAttribute>(); if (member.Type() == typeof(bool)) { SetValue( obj, member, param.Keys.Any(name => rawArgs.GetBoolean(name)) ); } else { string matchingKey = rawArgs.GetMatchingKey(param.Keys); if (matchingKey != null) { SetValue(obj, member, param, matchingKey, rawArgs[matchingKey]); } else if (ParamRequired <T>(defaultObject, param, member)) { // Required parameter missing throw new ParameterMissingException(param); } } } /* * Set positional arguments */ foreach (MemberInfo member in Utils.GetParameterMembers <T, PositionalParameterAttribute>(bindingFlags)) { PositionalParameterAttribute param = member.GetCustomAttribute <PositionalParameterAttribute>(); if (param.Index < rawArgs.PositionalArguments) { SetValue(obj, member, param, param.Name, rawArgs[(int)param.Index]); } else if (ParamRequired <T>(defaultObject, param, member)) { // Required parameter missing throw new ParameterMissingException(param); } } /* * Set the positional arguments list */ foreach (MemberInfo member in Utils.GetParameterMembers <T, PositionalParameterListAttribute>(bindingFlags)) { if (member.Type().IsArray) { SetValue(obj, member, rawArgs.GetPositionalArguments().ToArray()); } else { SetValue(obj, member, rawArgs.GetPositionalArguments()); } } /* * Check if a command was entered */ if (rawArgs.Command != null) { string[] commandArgs = args.Skip(rawArgs.CommandIndex + 1).ToArray(); MemberInfo commandMember = Utils.GetCommandWithName <T>(rawArgs.Command, bindingFlags); Type commandType = commandMember.Type(); Type parserType = typeof(CommanderParser <>).MakeGenericType(commandType); object parser = Activator.CreateInstance(parserType, commandArgs); // Set options parserType.GetTypeInfo().GetMethod("Separators", new Type[] { typeof(Separators) }) .Invoke(parser, new object[] { separators }); parserType.GetTypeInfo().GetMethod("Bindings", new Type[] { typeof(BindingFlags) }) .Invoke(parser, new object[] { bindingFlags }); // Parse the command object command = parserType.GetTypeInfo().GetMethod("Parse", new Type[] { typeof(string[]) }) .Invoke(parser, new object[] { commandArgs }); // Set the command back to the object SetValue(obj, commandMember, command); // Call the command handlers if (typeof(ICommand).GetTypeInfo().IsAssignableFrom(commandType)) { // First the handler on the command object, if it implements ICommand (command as ICommand).Execute(obj); } foreach (MethodInfo handler in Utils.GetMethods <T, CommandHandlerAttribute>(bindingFlags)) { // Then check if the parent object has any handler for this command ParameterInfo[] handlerParams = handler.GetParameters(); if (handlerParams.Length == 1 && handlerParams[0].ParameterType == commandType) { handler.Invoke(obj, new object[] { command }); } } } else if (Utils.GetCommandNames <T>(bindingFlags).Count > 0) { // The are commands, but no command was passed throw new CommandMissingException(); } return(obj); }