/// <summary> /// Generates a new parser that uses the given registry, args, metadata, context, and ID to run /// </summary> /// <param name="registry">Registry from which the parser will obtain <see cref="IObjectConverter"/>s</param> /// <param name="input">The original input string</param> /// <param name="additionalArgs">Any additional arguments to be added to the end of the argument list</param> /// <param name="metadata">CommandMetadata containing information used to parse and execute</param> /// <param name="exeData"><see cref="CommandExecutorData"/> containing the data required for execution</param> /// <param name="ctx">Context object passed to the executed command, and an <see cref="IObjectConverter"/>s that are used</param> /// <param name="callback">Reference to a method used as a callback when processing completes</param> public Parser(CommandRegistry registry, string input, IEnumerable <object> additionalArgs, CommandMetadata metadata, CommandExecutorData exeData, IContextObject ctx, InputResultDelegate callback) : base(registry, input, additionalArgs, metadata, exeData, ctx, callback) { }
/// <summary> /// Ensures the given parameters follow the parameter rules /// </summary> /// <exception cref="CommandParsingException">Thrown if a parameter does not follow command parameter rules</exception> public void CheckParameterStructure(CommandExecutorData executor, IEnumerable<ParameterInfo> parameters) { bool optionalFound = false; bool unknownLengthFound = false; requiredArgumentCount = 0; Dictionary<ParameterInfo, CommandParameterAttribute> paramData = new Dictionary<ParameterInfo, CommandParameterAttribute>(); foreach (ParameterInfo param in parameters) { CommandParameterAttribute attr = param.GetCustomAttribute<CommandParameterAttribute>(); if (attr == null) { attr = new CommandParameterAttribute(param.IsOptional); } paramData.Add(param, attr); if (optionalFound && !attr.Optional) { throw new CommandParsingException( ParserFailReason.InvalidParameter, $"Parameter '{param.Name}' is required, but follows an optional parameter." ); } if (unknownLengthFound) { throw new CommandParsingException( ParserFailReason.InvalidParameter, $"Parameter '{param.Name}' follows an unknown-length parameter." ); } if (attr.Optional) { optionalFound = true; } if (attr.Repetitions < 1) { unknownLengthFound = true; requiredArgumentCount = -1; } if (!attr.Optional) { requiredArgumentCount += attr.Repetitions; } } executor.ParameterData = paramData; }
/// <summary> /// Generates a new parser that uses the given registry, args, metadata, context, and ID to run /// </summary> /// <param name="registry">Registry from which the parser will obtain <see cref="IObjectConverter"/>s</param> /// <param name="input">The original input string</param> /// <param name="additionalArgs">Enumerable of objects to be parsed</param> /// <param name="metadata">CommandMetadata containing information used to parse and execute</param> /// <param name="exeData"><see cref="CommandExecutorData"/> containing the data required for execution</param> /// <param name="ctx">Context object passed to the executed command, and an <see cref="IObjectConverter"/>s that are used</param> /// <param name="callback">Reference to a method to be invoked when parsing completes</param> public AbstractParser(CommandRegistry registry, string input, IEnumerable <object> additionalArgs, CommandMetadata metadata, CommandExecutorData exeData, IContextObject ctx, InputResultDelegate callback) { Registry = registry; Input = input; AdditionalArgs = additionalArgs; Metadata = metadata; ExecutorData = exeData; Context = ctx; Callback = callback; }
/// <summary> /// Ensures the executing method follows the command executor method rules /// </summary> /// <exception cref="CommandParsingException">Thrown if the executor does not follow command executor method rules</exception> public ParameterInfo[] VerifyMethodStructure(CommandExecutorData data) { MethodInfo mInfo = data.ExecutingMethod; if (!data.AsyncExecution) { if (mInfo.ReturnType != typeof(object)) { throw new CommandParsingException( ParserFailReason.MalformedExecutor, $"Executor method '{mInfo.Name}' of command '{_type.Name}' does not return '{nameof(Object)}'." ); } } else { if (mInfo.ReturnType != typeof(Task<object>)) { throw new CommandParsingException( ParserFailReason.MalformedExecutor, $"Executor method '{mInfo.Name}' of command '{_type.Name}' does not return '{nameof(Task<object>)}'." ); } } ParameterInfo[] parameters = mInfo.GetParameters(); Exception inner = null; if (parameters.Length < 1) { inner = new Exception("Method defines no parameters, but requires at least one."); } else if (!typeof(IContextObject).GetTypeInfo().IsAssignableFrom(parameters[0].ParameterType)) { inner = new InvalidCastException($"Parameter '{parameters[0].Name}' of type '{parameters[0].ParameterType.Name}' must be castable to type '{nameof(IContextObject)}'."); } if (inner != null) { throw new CommandParsingException( ParserFailReason.MalformedExecutor, $"Executor method '{mInfo.Name}' of command '{_type.Name}' does not meet the required parameter rulings.", inner ); } return parameters; }
/// <summary> /// Attempts to switch to a subcommand instead of the main executor /// </summary> protected override void AttemptSwitchToSubcommand() { if (!ExecutorData.HasSubcommands || Input.Length == 0) { return; } CommandExecutorData subcommand = ExecutorData.Subcommands.FirstOrDefault( sub => sub.ExecutorAttribute.CommandMatcher.Matches(Input) ); if (subcommand != null) { ExecutorData = subcommand; //Remove the subcommand name Input = subcommand.ExecutorAttribute.CommandMatcher.RemoveMatchedString(Input); } }