/// <summary> /// Add command modules from an <see cref="Assembly"/>. /// </summary> /// <param name="assembly">The <see cref="Assembly"/> containing command modules.</param> /// <param name="services">The <see cref="IServiceProvider"/> for your dependency injection solution if using one; otherwise, pass <c>null</c>.</param> /// <returns> /// A task that represents the asynchronous operation for adding the command modules. The task result /// contains an enumerable collection of modules added. /// </returns> public async Task <IEnumerable <ModuleInfo> > AddModulesAsync(Assembly assembly, IServiceProvider services) { services = services ?? EmptyServiceProvider.Instance; await _moduleLock.WaitAsync().ConfigureAwait(false); try { var types = await ModuleClassBuilder.SearchAsync(assembly, this).ConfigureAwait(false); var moduleDefs = await ModuleClassBuilder.BuildAsync(types, this, services).ConfigureAwait(false); foreach (var info in moduleDefs) { _typedModuleDefs[info.Key] = info.Value; LoadModuleInternal(info.Value); } return(moduleDefs.Select(x => x.Value).ToImmutableArray()); } finally { _moduleLock.Release(); } }
/// <summary> /// Adds a command module from a <see cref="Type" />. /// </summary> /// <param name="type">The type of module.</param> /// <param name="services">The <see cref="IServiceProvider" /> for your dependency injection solution if using one; otherwise, pass <c>null</c> .</param> /// <exception cref="ArgumentException">This module has already been added.</exception> /// <exception cref="InvalidOperationException"> /// The <see cref="ModuleInfo"/> fails to be built; an invalid type may have been provided. /// </exception> /// <returns> /// A task that represents the asynchronous operation for adding the module. The task result contains the /// built module. /// </returns> public async Task <ModuleInfo> AddModuleAsync(Type type, IServiceProvider services) { services = services ?? EmptyServiceProvider.Instance; await _moduleLock.WaitAsync().ConfigureAwait(false); try { var typeInfo = type.GetTypeInfo(); if (_typedModuleDefs.ContainsKey(type)) { throw new ArgumentException("This module has already been added."); } var module = (await ModuleClassBuilder.BuildAsync(this, services, typeInfo).ConfigureAwait(false)).FirstOrDefault(); if (module.Value == default(ModuleInfo)) { throw new InvalidOperationException($"Could not build the module {type.FullName}, did you pass an invalid type?"); } _typedModuleDefs[module.Key] = module.Value; return(LoadModuleInternal(module.Value)); } finally { _moduleLock.Release(); } }
public override async Task <TypeReaderResult> ReadAsync(ITurnContext context, string input, IServiceProvider services) { var result = new T(); var state = ReadState.LookingForParameter; int beginRead = 0, currentRead = 0; while (state != ReadState.End) { try { var prop = Read(out var arg); var propVal = await ReadArgumentAsync(prop, arg).ConfigureAwait(false); if (propVal != null) { prop.SetMethod.Invoke(result, new[] { propVal }); } else { return(TypeReaderResult.FromError(CommandError.ParseFailed, $"Could not parse the argument for the parameter '{prop.Name}' as type '{prop.PropertyType}'.")); } } catch (Exception ex) { //TODO: use the Exception overload after a rebase on latest return(TypeReaderResult.FromError(CommandError.Exception, ex.Message)); } } return(TypeReaderResult.FromSuccess(result)); PropertyInfo Read(out string arg) { string currentParam = null; char match = '\0'; for (; currentRead < input.Length; currentRead++) { var currentChar = input[currentRead]; switch (state) { case ReadState.LookingForParameter: if (Char.IsWhiteSpace(currentChar)) { continue; } else { beginRead = currentRead; state = ReadState.InParameter; } break; case ReadState.InParameter: if (currentChar != ':') { continue; } else { currentParam = input.Substring(beginRead, currentRead - beginRead); state = ReadState.LookingForArgument; } break; case ReadState.LookingForArgument: if (Char.IsWhiteSpace(currentChar)) { continue; } else { beginRead = currentRead; state = (QuotationAliasUtils.GetDefaultAliasMap.TryGetValue(currentChar, out match)) ? ReadState.InQuotedArgument : ReadState.InArgument; } break; case ReadState.InArgument: if (!Char.IsWhiteSpace(currentChar)) { continue; } else { return(GetPropAndValue(out arg)); } case ReadState.InQuotedArgument: if (currentChar != match) { continue; } else { return(GetPropAndValue(out arg)); } } } if (currentParam == null) { throw new InvalidOperationException("No parameter name was read."); } return(GetPropAndValue(out arg)); PropertyInfo GetPropAndValue(out string argv) { bool quoted = state == ReadState.InQuotedArgument; state = (currentRead == (quoted ? input.Length - 1 : input.Length)) ? ReadState.End : ReadState.LookingForParameter; if (quoted) { argv = input.Substring(beginRead + 1, currentRead - beginRead - 1).Trim(); currentRead++; } else { argv = input.Substring(beginRead, currentRead - beginRead); } return(_tProps[currentParam]); } } async Task <object> ReadArgumentAsync(PropertyInfo prop, string arg) { var elemType = prop.PropertyType; bool isCollection = false; if (elemType.GetTypeInfo().IsGenericType&& elemType.GetGenericTypeDefinition() == typeof(IEnumerable <>)) { elemType = prop.PropertyType.GenericTypeArguments[0]; isCollection = true; } var overridden = prop.GetCustomAttribute <OverrideTypeReaderAttribute>(); var reader = (overridden != null) ? ModuleClassBuilder.GetTypeReader(_commands, elemType, overridden.TypeReader, services) : (_commands.GetDefaultTypeReader(elemType) ?? _commands.GetTypeReaders(elemType).FirstOrDefault().Value); if (reader != null) { if (isCollection) { var method = _readMultipleMethod.MakeGenericMethod(elemType); var task = (Task <IEnumerable>)method.Invoke(null, new object[] { reader, context, arg.Split(','), services }); return(await task.ConfigureAwait(false)); } else { return(await ReadSingle(reader, context, arg, services).ConfigureAwait(false)); } } return(null); } }