Replaced DependencyMap with ServiceCollection and IServiceProvider (#625)
This commit is contained in:
@@ -1,11 +1,12 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace Discord.Commands
|
namespace Discord.Commands
|
||||||
{
|
{
|
||||||
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = true, Inherited = true)]
|
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = true, Inherited = true)]
|
||||||
public abstract class ParameterPreconditionAttribute : Attribute
|
public abstract class ParameterPreconditionAttribute : Attribute
|
||||||
{
|
{
|
||||||
public abstract Task<PreconditionResult> CheckPermissions(ICommandContext context, ParameterInfo parameter, object value, IDependencyMap map);
|
public abstract Task<PreconditionResult> CheckPermissions(ICommandContext context, ParameterInfo parameter, object value, IServiceProvider services);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,6 @@ namespace Discord.Commands
|
|||||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
|
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
|
||||||
public abstract class PreconditionAttribute : Attribute
|
public abstract class PreconditionAttribute : Attribute
|
||||||
{
|
{
|
||||||
public abstract Task<PreconditionResult> CheckPermissions(ICommandContext context, CommandInfo command, IDependencyMap map);
|
public abstract Task<PreconditionResult> CheckPermissions(ICommandContext context, CommandInfo command, IServiceProvider services);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace Discord.Commands
|
namespace Discord.Commands
|
||||||
{
|
{
|
||||||
@@ -41,7 +42,7 @@ namespace Discord.Commands
|
|||||||
GuildPermission = null;
|
GuildPermission = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task<PreconditionResult> CheckPermissions(ICommandContext context, CommandInfo command, IDependencyMap map)
|
public override async Task<PreconditionResult> CheckPermissions(ICommandContext context, CommandInfo command, IServiceProvider services)
|
||||||
{
|
{
|
||||||
var guildUser = await context.Guild.GetCurrentUserAsync();
|
var guildUser = await context.Guild.GetCurrentUserAsync();
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace Discord.Commands
|
namespace Discord.Commands
|
||||||
{
|
{
|
||||||
@@ -37,7 +38,7 @@ namespace Discord.Commands
|
|||||||
Contexts = contexts;
|
Contexts = contexts;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Task<PreconditionResult> CheckPermissions(ICommandContext context, CommandInfo command, IDependencyMap map)
|
public override Task<PreconditionResult> CheckPermissions(ICommandContext context, CommandInfo command, IServiceProvider services)
|
||||||
{
|
{
|
||||||
bool isValid = false;
|
bool isValid = false;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace Discord.Commands
|
namespace Discord.Commands
|
||||||
{
|
{
|
||||||
@@ -10,7 +11,7 @@ namespace Discord.Commands
|
|||||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
|
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
|
||||||
public class RequireOwnerAttribute : PreconditionAttribute
|
public class RequireOwnerAttribute : PreconditionAttribute
|
||||||
{
|
{
|
||||||
public override async Task<PreconditionResult> CheckPermissions(ICommandContext context, CommandInfo command, IDependencyMap map)
|
public override async Task<PreconditionResult> CheckPermissions(ICommandContext context, CommandInfo command, IServiceProvider services)
|
||||||
{
|
{
|
||||||
switch (context.Client.TokenType)
|
switch (context.Client.TokenType)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace Discord.Commands
|
namespace Discord.Commands
|
||||||
{
|
{
|
||||||
@@ -42,7 +43,7 @@ namespace Discord.Commands
|
|||||||
GuildPermission = null;
|
GuildPermission = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Task<PreconditionResult> CheckPermissions(ICommandContext context, CommandInfo command, IDependencyMap map)
|
public override Task<PreconditionResult> CheckPermissions(ICommandContext context, CommandInfo command, IServiceProvider services)
|
||||||
{
|
{
|
||||||
var guildUser = context.User as IGuildUser;
|
var guildUser = context.User as IGuildUser;
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace Discord.Commands.Builders
|
namespace Discord.Commands.Builders
|
||||||
{
|
{
|
||||||
@@ -12,7 +13,7 @@ namespace Discord.Commands.Builders
|
|||||||
private readonly List<string> _aliases;
|
private readonly List<string> _aliases;
|
||||||
|
|
||||||
public ModuleBuilder Module { get; }
|
public ModuleBuilder Module { get; }
|
||||||
internal Func<ICommandContext, object[], IDependencyMap, Task> Callback { get; set; }
|
internal Func<ICommandContext, object[], IServiceProvider, Task> Callback { get; set; }
|
||||||
|
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string Summary { get; set; }
|
public string Summary { get; set; }
|
||||||
@@ -35,7 +36,7 @@ namespace Discord.Commands.Builders
|
|||||||
_aliases = new List<string>();
|
_aliases = new List<string>();
|
||||||
}
|
}
|
||||||
//User-defined
|
//User-defined
|
||||||
internal CommandBuilder(ModuleBuilder module, string primaryAlias, Func<ICommandContext, object[], IDependencyMap, Task> callback)
|
internal CommandBuilder(ModuleBuilder module, string primaryAlias, Func<ICommandContext, object[], IServiceProvider, Task> callback)
|
||||||
: this(module)
|
: this(module)
|
||||||
{
|
{
|
||||||
Discord.Preconditions.NotNull(primaryAlias, nameof(primaryAlias));
|
Discord.Preconditions.NotNull(primaryAlias, nameof(primaryAlias));
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace Discord.Commands.Builders
|
namespace Discord.Commands.Builders
|
||||||
{
|
{
|
||||||
@@ -73,7 +74,7 @@ namespace Discord.Commands.Builders
|
|||||||
_preconditions.Add(precondition);
|
_preconditions.Add(precondition);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
public ModuleBuilder AddCommand(string primaryAlias, Func<ICommandContext, object[], IDependencyMap, Task> callback, Action<CommandBuilder> createFunc)
|
public ModuleBuilder AddCommand(string primaryAlias, Func<ICommandContext, object[], IServiceProvider, Task> callback, Action<CommandBuilder> createFunc)
|
||||||
{
|
{
|
||||||
var builder = new CommandBuilder(this, primaryAlias, callback);
|
var builder = new CommandBuilder(this, primaryAlias, callback);
|
||||||
createFunc(builder);
|
createFunc(builder);
|
||||||
|
|||||||
@@ -243,7 +243,7 @@ namespace Discord.Commands
|
|||||||
}
|
}
|
||||||
|
|
||||||
//We dont have a cached type reader, create one
|
//We dont have a cached type reader, create one
|
||||||
reader = ReflectionUtils.CreateObject<TypeReader>(typeReaderType.GetTypeInfo(), service, DependencyMap.Empty);
|
reader = ReflectionUtils.CreateObject<TypeReader>(typeReaderType.GetTypeInfo(), service, EmptyServiceProvider.Instance);
|
||||||
service.AddTypeReader(paramType, reader);
|
service.AddTypeReader(paramType, reader);
|
||||||
|
|
||||||
return reader;
|
return reader;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace Discord.Commands
|
namespace Discord.Commands
|
||||||
{
|
{
|
||||||
@@ -14,13 +16,13 @@ namespace Discord.Commands
|
|||||||
Alias = alias;
|
Alias = alias;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<PreconditionResult> CheckPreconditionsAsync(ICommandContext context, IDependencyMap map = null)
|
public Task<PreconditionResult> CheckPreconditionsAsync(ICommandContext context, IServiceProvider services = null)
|
||||||
=> Command.CheckPreconditionsAsync(context, map);
|
=> Command.CheckPreconditionsAsync(context, services);
|
||||||
public Task<ParseResult> ParseAsync(ICommandContext context, SearchResult searchResult, PreconditionResult? preconditionResult = null)
|
public Task<ParseResult> ParseAsync(ICommandContext context, SearchResult searchResult, PreconditionResult? preconditionResult = null)
|
||||||
=> Command.ParseAsync(context, Alias.Length, searchResult, preconditionResult);
|
=> Command.ParseAsync(context, Alias.Length, searchResult, preconditionResult);
|
||||||
public Task<ExecuteResult> ExecuteAsync(ICommandContext context, IEnumerable<object> argList, IEnumerable<object> paramList, IDependencyMap map)
|
public Task<ExecuteResult> ExecuteAsync(ICommandContext context, IEnumerable<object> argList, IEnumerable<object> paramList, IServiceProvider services)
|
||||||
=> Command.ExecuteAsync(context, argList, paramList, map);
|
=> Command.ExecuteAsync(context, argList, paramList, services);
|
||||||
public Task<ExecuteResult> ExecuteAsync(ICommandContext context, ParseResult parseResult, IDependencyMap map)
|
public Task<ExecuteResult> ExecuteAsync(ICommandContext context, ParseResult parseResult, IServiceProvider services)
|
||||||
=> Command.ExecuteAsync(context, parseResult, map);
|
=> Command.ExecuteAsync(context, parseResult, services);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ using System.Linq;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace Discord.Commands
|
namespace Discord.Commands
|
||||||
{
|
{
|
||||||
@@ -247,11 +248,11 @@ namespace Discord.Commands
|
|||||||
return SearchResult.FromError(CommandError.UnknownCommand, "Unknown command.");
|
return SearchResult.FromError(CommandError.UnknownCommand, "Unknown command.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IResult> ExecuteAsync(ICommandContext context, int argPos, IDependencyMap dependencyMap = null, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception)
|
public Task<IResult> ExecuteAsync(ICommandContext context, int argPos, IServiceProvider services = null, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception)
|
||||||
=> ExecuteAsync(context, context.Message.Content.Substring(argPos), dependencyMap, multiMatchHandling);
|
=> ExecuteAsync(context, context.Message.Content.Substring(argPos), services, multiMatchHandling);
|
||||||
public async Task<IResult> ExecuteAsync(ICommandContext context, string input, IDependencyMap dependencyMap = null, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception)
|
public async Task<IResult> ExecuteAsync(ICommandContext context, string input, IServiceProvider services = null, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception)
|
||||||
{
|
{
|
||||||
dependencyMap = dependencyMap ?? DependencyMap.Empty;
|
services = services ?? EmptyServiceProvider.Instance;
|
||||||
|
|
||||||
var searchResult = Search(context, input);
|
var searchResult = Search(context, input);
|
||||||
if (!searchResult.IsSuccess)
|
if (!searchResult.IsSuccess)
|
||||||
@@ -260,7 +261,7 @@ namespace Discord.Commands
|
|||||||
var commands = searchResult.Commands;
|
var commands = searchResult.Commands;
|
||||||
for (int i = 0; i < commands.Count; i++)
|
for (int i = 0; i < commands.Count; i++)
|
||||||
{
|
{
|
||||||
var preconditionResult = await commands[i].CheckPreconditionsAsync(context, dependencyMap).ConfigureAwait(false);
|
var preconditionResult = await commands[i].CheckPreconditionsAsync(context, services).ConfigureAwait(false);
|
||||||
if (!preconditionResult.IsSuccess)
|
if (!preconditionResult.IsSuccess)
|
||||||
{
|
{
|
||||||
if (commands.Count == 1)
|
if (commands.Count == 1)
|
||||||
@@ -294,10 +295,19 @@ namespace Discord.Commands
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return await commands[i].ExecuteAsync(context, parseResult, dependencyMap).ConfigureAwait(false);
|
return await commands[i].ExecuteAsync(context, parseResult, services).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return SearchResult.FromError(CommandError.UnknownCommand, "This input does not match any overload.");
|
return SearchResult.FromError(CommandError.UnknownCommand, "This input does not match any overload.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ServiceCollection CreateServiceCollection()
|
||||||
|
{
|
||||||
|
var serviceCollection = new ServiceCollection();
|
||||||
|
serviceCollection.AddSingleton<CommandService>(this);
|
||||||
|
serviceCollection.AddSingleton<IServiceCollection>(serviceCollection);
|
||||||
|
serviceCollection.AddSingleton<ServiceCollection>(serviceCollection);
|
||||||
|
return serviceCollection;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,102 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace Discord.Commands
|
|
||||||
{
|
|
||||||
public class DependencyMap : IDependencyMap
|
|
||||||
{
|
|
||||||
private static readonly Type[] _typeBlacklist = new[] {
|
|
||||||
typeof(IDependencyMap),
|
|
||||||
typeof(CommandService)
|
|
||||||
};
|
|
||||||
|
|
||||||
private Dictionary<Type, Func<object>> map;
|
|
||||||
|
|
||||||
public static DependencyMap Empty => new DependencyMap();
|
|
||||||
|
|
||||||
public DependencyMap()
|
|
||||||
{
|
|
||||||
map = new Dictionary<Type, Func<object>>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void Add<T>(T obj) where T : class
|
|
||||||
=> AddFactory(() => obj);
|
|
||||||
/// <inheritdoc />
|
|
||||||
public bool TryAdd<T>(T obj) where T : class
|
|
||||||
=> TryAddFactory(() => obj);
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void AddTransient<T>() where T : class, new()
|
|
||||||
=> AddFactory(() => new T());
|
|
||||||
/// <inheritdoc />
|
|
||||||
public bool TryAddTransient<T>() where T : class, new()
|
|
||||||
=> TryAddFactory(() => new T());
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void AddTransient<TKey, TImpl>() where TKey : class
|
|
||||||
where TImpl : class, TKey, new()
|
|
||||||
=> AddFactory<TKey>(() => new TImpl());
|
|
||||||
public bool TryAddTransient<TKey, TImpl>() where TKey : class
|
|
||||||
where TImpl : class, TKey, new()
|
|
||||||
=> TryAddFactory<TKey>(() => new TImpl());
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void AddFactory<T>(Func<T> factory) where T : class
|
|
||||||
{
|
|
||||||
if (!TryAddFactory(factory))
|
|
||||||
throw new InvalidOperationException($"The dependency map already contains \"{typeof(T).FullName}\"");
|
|
||||||
}
|
|
||||||
/// <inheritdoc />
|
|
||||||
public bool TryAddFactory<T>(Func<T> factory) where T : class
|
|
||||||
{
|
|
||||||
var type = typeof(T);
|
|
||||||
if (_typeBlacklist.Contains(type) || map.ContainsKey(type))
|
|
||||||
return false;
|
|
||||||
map.Add(type, factory);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public T Get<T>() where T : class
|
|
||||||
{
|
|
||||||
return (T)Get(typeof(T));
|
|
||||||
}
|
|
||||||
/// <inheritdoc />
|
|
||||||
public object Get(Type t)
|
|
||||||
{
|
|
||||||
object result;
|
|
||||||
if (!TryGet(t, out result))
|
|
||||||
throw new KeyNotFoundException($"The dependency map does not contain \"{t.FullName}\"");
|
|
||||||
else
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public bool TryGet<T>(out T result) where T : class
|
|
||||||
{
|
|
||||||
object untypedResult;
|
|
||||||
if (TryGet(typeof(T), out untypedResult))
|
|
||||||
{
|
|
||||||
result = (T)untypedResult;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result = default(T);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// <inheritdoc />
|
|
||||||
public bool TryGet(Type t, out object result)
|
|
||||||
{
|
|
||||||
Func<object> func;
|
|
||||||
if (map.TryGetValue(t, out func))
|
|
||||||
{
|
|
||||||
result = func();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
result = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Discord.Commands
|
|
||||||
{
|
|
||||||
public interface IDependencyMap
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Add an instance of a service to be injected.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of service.</typeparam>
|
|
||||||
/// <param name="obj">The instance of a service.</param>
|
|
||||||
void Add<T>(T obj) where T : class;
|
|
||||||
/// <summary>
|
|
||||||
/// Tries to add an instance of a service to be injected.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of service.</typeparam>
|
|
||||||
/// <param name="obj">The instance of a service.</param>
|
|
||||||
/// <returns>A bool, indicating if the service was successfully added to the DependencyMap.</returns>
|
|
||||||
bool TryAdd<T>(T obj) where T : class;
|
|
||||||
/// <summary>
|
|
||||||
/// Add a service that will be injected by a new instance every time.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of instance to inject.</typeparam>
|
|
||||||
void AddTransient<T>() where T : class, new();
|
|
||||||
/// <summary>
|
|
||||||
/// Tries to add a service that will be injected by a new instance every time.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of instance to inject.</typeparam>
|
|
||||||
/// <returns>A bool, indicating if the service was successfully added to the DependencyMap.</returns>
|
|
||||||
bool TryAddTransient<T>() where T : class, new();
|
|
||||||
/// <summary>
|
|
||||||
/// Add a service that will be injected by a new instance every time.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TKey">The type to look for when injecting.</typeparam>
|
|
||||||
/// <typeparam name="TImpl">The type to inject when injecting.</typeparam>
|
|
||||||
/// <example>
|
|
||||||
/// map.AddTransient<IService, Service>
|
|
||||||
/// </example>
|
|
||||||
void AddTransient<TKey, TImpl>() where TKey: class where TImpl : class, TKey, new();
|
|
||||||
/// <summary>
|
|
||||||
/// Tries to add a service that will be injected by a new instance every time.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TKey">The type to look for when injecting.</typeparam>
|
|
||||||
/// <typeparam name="TImpl">The type to inject when injecting.</typeparam>
|
|
||||||
/// <returns>A bool, indicating if the service was successfully added to the DependencyMap.</returns>
|
|
||||||
bool TryAddTransient<TKey, TImpl>() where TKey : class where TImpl : class, TKey, new();
|
|
||||||
/// <summary>
|
|
||||||
/// Add a service that will be injected by a factory.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type to look for when injecting.</typeparam>
|
|
||||||
/// <param name="factory">The factory that returns a type of this service.</param>
|
|
||||||
void AddFactory<T>(Func<T> factory) where T : class;
|
|
||||||
/// <summary>
|
|
||||||
/// Tries to add a service that will be injected by a factory.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type to look for when injecting.</typeparam>
|
|
||||||
/// <param name="factory">The factory that returns a type of this service.</param>
|
|
||||||
/// <returns>A bool, indicating if the service was successfully added to the DependencyMap.</returns>
|
|
||||||
bool TryAddFactory<T>(Func<T> factory) where T : class;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Pull an object from the map.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of service.</typeparam>
|
|
||||||
/// <returns>An instance of this service.</returns>
|
|
||||||
T Get<T>() where T : class;
|
|
||||||
/// <summary>
|
|
||||||
/// Try to pull an object from the map.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of service.</typeparam>
|
|
||||||
/// <param name="result">The instance of this service.</param>
|
|
||||||
/// <returns>Whether or not this object could be found in the map.</returns>
|
|
||||||
bool TryGet<T>(out T result) where T : class;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Pull an object from the map.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="t">The type of service.</param>
|
|
||||||
/// <returns>An instance of this service.</returns>
|
|
||||||
object Get(Type t);
|
|
||||||
/// <summary>
|
|
||||||
/// Try to pull an object from the map.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="t">The type of service.</param>
|
|
||||||
/// <param name="result">An instance of this service.</param>
|
|
||||||
/// <returns>Whether or not this object could be found in the map.</returns>
|
|
||||||
bool TryGet(Type t, out object result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,4 +9,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Discord.Net.Core\Discord.Net.Core.csproj" />
|
<ProjectReference Include="..\Discord.Net.Core\Discord.Net.Core.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="1.1.0" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
11
src/Discord.Net.Commands/EmptyServiceProvider.cs
Normal file
11
src/Discord.Net.Commands/EmptyServiceProvider.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Discord.Commands
|
||||||
|
{
|
||||||
|
internal class EmptyServiceProvider : IServiceProvider
|
||||||
|
{
|
||||||
|
public static readonly EmptyServiceProvider Instance = new EmptyServiceProvider();
|
||||||
|
|
||||||
|
public object GetService(Type serviceType) => null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ using System.Linq;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.ExceptionServices;
|
using System.Runtime.ExceptionServices;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace Discord.Commands
|
namespace Discord.Commands
|
||||||
{
|
{
|
||||||
@@ -17,7 +18,7 @@ namespace Discord.Commands
|
|||||||
private static readonly System.Reflection.MethodInfo _convertParamsMethod = typeof(CommandInfo).GetTypeInfo().GetDeclaredMethod(nameof(ConvertParamsList));
|
private static readonly System.Reflection.MethodInfo _convertParamsMethod = typeof(CommandInfo).GetTypeInfo().GetDeclaredMethod(nameof(ConvertParamsList));
|
||||||
private static readonly ConcurrentDictionary<Type, Func<IEnumerable<object>, object>> _arrayConverters = new ConcurrentDictionary<Type, Func<IEnumerable<object>, object>>();
|
private static readonly ConcurrentDictionary<Type, Func<IEnumerable<object>, object>> _arrayConverters = new ConcurrentDictionary<Type, Func<IEnumerable<object>, object>>();
|
||||||
|
|
||||||
private readonly Func<ICommandContext, object[], IDependencyMap, Task> _action;
|
private readonly Func<ICommandContext, object[], IServiceProvider, Task> _action;
|
||||||
|
|
||||||
public ModuleInfo Module { get; }
|
public ModuleInfo Module { get; }
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
@@ -63,21 +64,20 @@ namespace Discord.Commands
|
|||||||
_action = builder.Callback;
|
_action = builder.Callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<PreconditionResult> CheckPreconditionsAsync(ICommandContext context, IDependencyMap map = null)
|
public async Task<PreconditionResult> CheckPreconditionsAsync(ICommandContext context, IServiceProvider services = null)
|
||||||
{
|
{
|
||||||
if (map == null)
|
services = services ?? EmptyServiceProvider.Instance;
|
||||||
map = DependencyMap.Empty;
|
|
||||||
|
|
||||||
foreach (PreconditionAttribute precondition in Module.Preconditions)
|
foreach (PreconditionAttribute precondition in Module.Preconditions)
|
||||||
{
|
{
|
||||||
var result = await precondition.CheckPermissions(context, this, map).ConfigureAwait(false);
|
var result = await precondition.CheckPermissions(context, this, services).ConfigureAwait(false);
|
||||||
if (!result.IsSuccess)
|
if (!result.IsSuccess)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (PreconditionAttribute precondition in Preconditions)
|
foreach (PreconditionAttribute precondition in Preconditions)
|
||||||
{
|
{
|
||||||
var result = await precondition.CheckPermissions(context, this, map).ConfigureAwait(false);
|
var result = await precondition.CheckPermissions(context, this, services).ConfigureAwait(false);
|
||||||
if (!result.IsSuccess)
|
if (!result.IsSuccess)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -96,7 +96,7 @@ namespace Discord.Commands
|
|||||||
return await CommandParser.ParseArgs(this, context, input, 0).ConfigureAwait(false);
|
return await CommandParser.ParseArgs(this, context, input, 0).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<ExecuteResult> ExecuteAsync(ICommandContext context, ParseResult parseResult, IDependencyMap map)
|
public Task<ExecuteResult> ExecuteAsync(ICommandContext context, ParseResult parseResult, IServiceProvider services)
|
||||||
{
|
{
|
||||||
if (!parseResult.IsSuccess)
|
if (!parseResult.IsSuccess)
|
||||||
return Task.FromResult(ExecuteResult.FromError(parseResult));
|
return Task.FromResult(ExecuteResult.FromError(parseResult));
|
||||||
@@ -117,12 +117,11 @@ namespace Discord.Commands
|
|||||||
paramList[i] = parseResult.ParamValues[i].Values.First().Value;
|
paramList[i] = parseResult.ParamValues[i].Values.First().Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ExecuteAsync(context, argList, paramList, map);
|
return ExecuteAsync(context, argList, paramList, services);
|
||||||
}
|
}
|
||||||
public async Task<ExecuteResult> ExecuteAsync(ICommandContext context, IEnumerable<object> argList, IEnumerable<object> paramList, IDependencyMap map)
|
public async Task<ExecuteResult> ExecuteAsync(ICommandContext context, IEnumerable<object> argList, IEnumerable<object> paramList, IServiceProvider services)
|
||||||
{
|
{
|
||||||
if (map == null)
|
services = services ?? EmptyServiceProvider.Instance;
|
||||||
map = DependencyMap.Empty;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -132,7 +131,7 @@ namespace Discord.Commands
|
|||||||
{
|
{
|
||||||
var parameter = Parameters[position];
|
var parameter = Parameters[position];
|
||||||
var argument = args[position];
|
var argument = args[position];
|
||||||
var result = await parameter.CheckPreconditionsAsync(context, argument, map).ConfigureAwait(false);
|
var result = await parameter.CheckPreconditionsAsync(context, argument, services).ConfigureAwait(false);
|
||||||
if (!result.IsSuccess)
|
if (!result.IsSuccess)
|
||||||
return ExecuteResult.FromError(result);
|
return ExecuteResult.FromError(result);
|
||||||
}
|
}
|
||||||
@@ -140,12 +139,12 @@ namespace Discord.Commands
|
|||||||
switch (RunMode)
|
switch (RunMode)
|
||||||
{
|
{
|
||||||
case RunMode.Sync: //Always sync
|
case RunMode.Sync: //Always sync
|
||||||
await ExecuteAsyncInternal(context, args, map).ConfigureAwait(false);
|
await ExecuteAsyncInternal(context, args, services).ConfigureAwait(false);
|
||||||
break;
|
break;
|
||||||
case RunMode.Async: //Always async
|
case RunMode.Async: //Always async
|
||||||
var t2 = Task.Run(async () =>
|
var t2 = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
await ExecuteAsyncInternal(context, args, map).ConfigureAwait(false);
|
await ExecuteAsyncInternal(context, args, services).ConfigureAwait(false);
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -157,12 +156,12 @@ namespace Discord.Commands
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ExecuteAsyncInternal(ICommandContext context, object[] args, IDependencyMap map)
|
private async Task ExecuteAsyncInternal(ICommandContext context, object[] args, IServiceProvider services)
|
||||||
{
|
{
|
||||||
await Module.Service._cmdLogger.DebugAsync($"Executing {GetLogText(context)}").ConfigureAwait(false);
|
await Module.Service._cmdLogger.DebugAsync($"Executing {GetLogText(context)}").ConfigureAwait(false);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _action(context, args, map).ConfigureAwait(false);
|
await _action(context, args, services).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace Discord.Commands
|
namespace Discord.Commands
|
||||||
{
|
{
|
||||||
@@ -39,14 +40,13 @@ namespace Discord.Commands
|
|||||||
_reader = builder.TypeReader;
|
_reader = builder.TypeReader;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<PreconditionResult> CheckPreconditionsAsync(ICommandContext context, object arg, IDependencyMap map = null)
|
public async Task<PreconditionResult> CheckPreconditionsAsync(ICommandContext context, object arg, IServiceProvider services = null)
|
||||||
{
|
{
|
||||||
if (map == null)
|
services = EmptyServiceProvider.Instance;
|
||||||
map = DependencyMap.Empty;
|
|
||||||
|
|
||||||
foreach (var precondition in Preconditions)
|
foreach (var precondition in Preconditions)
|
||||||
{
|
{
|
||||||
var result = await precondition.CheckPermissions(context, this, arg, map).ConfigureAwait(false);
|
var result = await precondition.CheckPermissions(context, this, arg, services).ConfigureAwait(false);
|
||||||
if (!result.IsSuccess)
|
if (!result.IsSuccess)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,88 +2,79 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace Discord.Commands
|
namespace Discord.Commands
|
||||||
{
|
{
|
||||||
internal static class ReflectionUtils
|
internal static class ReflectionUtils
|
||||||
{
|
{
|
||||||
private static readonly TypeInfo objectTypeInfo = typeof(object).GetTypeInfo();
|
private static readonly TypeInfo _objectTypeInfo = typeof(object).GetTypeInfo();
|
||||||
|
|
||||||
internal static T CreateObject<T>(TypeInfo typeInfo, CommandService service, IDependencyMap map = null)
|
internal static T CreateObject<T>(TypeInfo typeInfo, CommandService commands, IServiceProvider services = null)
|
||||||
=> CreateBuilder<T>(typeInfo, service)(map);
|
=> CreateBuilder<T>(typeInfo, commands)(services);
|
||||||
|
internal static Func<IServiceProvider, T> CreateBuilder<T>(TypeInfo typeInfo, CommandService commands)
|
||||||
|
{
|
||||||
|
var constructor = GetConstructor(typeInfo);
|
||||||
|
var parameters = constructor.GetParameters();
|
||||||
|
var properties = GetProperties(typeInfo);
|
||||||
|
|
||||||
private static System.Reflection.PropertyInfo[] GetProperties(TypeInfo typeInfo)
|
return (services) =>
|
||||||
|
{
|
||||||
|
var args = new object[parameters.Length];
|
||||||
|
for (int i = 0; i < parameters.Length; i++)
|
||||||
|
args[i] = GetMember(commands, services, parameters[i].ParameterType, typeInfo);
|
||||||
|
var obj = InvokeConstructor<T>(constructor, args, typeInfo);
|
||||||
|
|
||||||
|
foreach(var property in properties)
|
||||||
|
property.SetValue(obj, GetMember(commands, services, property.PropertyType, typeInfo));
|
||||||
|
return obj;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
private static T InvokeConstructor<T>(ConstructorInfo constructor, object[] args, TypeInfo ownerType)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return (T)constructor.Invoke(args);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new Exception($"Failed to create \"{ownerType.FullName}\"", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ConstructorInfo GetConstructor(TypeInfo ownerType)
|
||||||
|
{
|
||||||
|
var constructors = ownerType.DeclaredConstructors.Where(x => !x.IsStatic).ToArray();
|
||||||
|
if (constructors.Length == 0)
|
||||||
|
throw new InvalidOperationException($"No constructor found for \"{ownerType.FullName}\"");
|
||||||
|
else if (constructors.Length > 1)
|
||||||
|
throw new InvalidOperationException($"Multiple constructors found for \"{ownerType.FullName}\"");
|
||||||
|
return constructors[0];
|
||||||
|
}
|
||||||
|
private static System.Reflection.PropertyInfo[] GetProperties(TypeInfo ownerType)
|
||||||
{
|
{
|
||||||
var result = new List<System.Reflection.PropertyInfo>();
|
var result = new List<System.Reflection.PropertyInfo>();
|
||||||
while (typeInfo != objectTypeInfo)
|
while (ownerType != _objectTypeInfo)
|
||||||
{
|
{
|
||||||
foreach (var prop in typeInfo.DeclaredProperties)
|
foreach (var prop in ownerType.DeclaredProperties)
|
||||||
{
|
{
|
||||||
if (prop.SetMethod?.IsPublic == true && prop.GetCustomAttribute<DontInjectAttribute>() == null)
|
if (prop.SetMethod?.IsPublic == true && prop.GetCustomAttribute<DontInjectAttribute>() == null)
|
||||||
result.Add(prop);
|
result.Add(prop);
|
||||||
}
|
}
|
||||||
typeInfo = typeInfo.BaseType.GetTypeInfo();
|
ownerType = ownerType.BaseType.GetTypeInfo();
|
||||||
}
|
}
|
||||||
return result.ToArray();
|
return result.ToArray();
|
||||||
}
|
}
|
||||||
|
private static object GetMember(CommandService commands, IServiceProvider services, Type memberType, TypeInfo ownerType)
|
||||||
internal static Func<IDependencyMap, T> CreateBuilder<T>(TypeInfo typeInfo, CommandService service)
|
|
||||||
{
|
{
|
||||||
var constructors = typeInfo.DeclaredConstructors.Where(x => !x.IsStatic).ToArray();
|
if (memberType == typeof(CommandService))
|
||||||
if (constructors.Length == 0)
|
return commands;
|
||||||
throw new InvalidOperationException($"No constructor found for \"{typeInfo.FullName}\"");
|
if (memberType == typeof(IServiceProvider) || memberType == services.GetType())
|
||||||
else if (constructors.Length > 1)
|
return services;
|
||||||
throw new InvalidOperationException($"Multiple constructors found for \"{typeInfo.FullName}\"");
|
var service = services?.GetService(memberType);
|
||||||
|
if (service != null)
|
||||||
var constructor = constructors[0];
|
return service;
|
||||||
System.Reflection.ParameterInfo[] parameters = constructor.GetParameters();
|
throw new InvalidOperationException($"Failed to create \"{ownerType.FullName}\", dependency \"{memberType.Name}\" was not found.");
|
||||||
System.Reflection.PropertyInfo[] properties = GetProperties(typeInfo)
|
|
||||||
.Where(p => p.SetMethod?.IsPublic == true && p.GetCustomAttribute<DontInjectAttribute>() == null)
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
return (map) =>
|
|
||||||
{
|
|
||||||
object[] args = new object[parameters.Length];
|
|
||||||
|
|
||||||
for (int i = 0; i < parameters.Length; i++)
|
|
||||||
{
|
|
||||||
var parameter = parameters[i];
|
|
||||||
args[i] = GetMember(parameter.ParameterType, map, service, typeInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
T obj;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
obj = (T)constructor.Invoke(args);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
throw new Exception($"Failed to create \"{typeInfo.FullName}\"", ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach(var property in properties)
|
|
||||||
{
|
|
||||||
property.SetValue(obj, GetMember(property.PropertyType, map, service, typeInfo));
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly TypeInfo _dependencyTypeInfo = typeof(IDependencyMap).GetTypeInfo();
|
|
||||||
|
|
||||||
internal static object GetMember(Type targetType, IDependencyMap map, CommandService service, TypeInfo baseType)
|
|
||||||
{
|
|
||||||
object arg;
|
|
||||||
if (map == null || !map.TryGet(targetType, out arg))
|
|
||||||
{
|
|
||||||
if (targetType == typeof(CommandService))
|
|
||||||
arg = service;
|
|
||||||
else if (targetType == typeof(IDependencyMap) || targetType == map.GetType())
|
|
||||||
arg = map;
|
|
||||||
else
|
|
||||||
throw new InvalidOperationException($"Failed to create \"{baseType.FullName}\", dependency \"{targetType.Name}\" was not found.");
|
|
||||||
}
|
|
||||||
return arg;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user