/// <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();
            }
        }
Esempio n. 3
0
        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);
            }
        }