@@ -5,6 +5,8 @@ VisualStudioVersion = 14.0.25123.0 | |||
MinimumVisualStudioVersion = 10.0.40219.1 | |||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net", "src\Discord.Net\Discord.Net.xproj", "{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}" | |||
EndProject | |||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net.Commands", "src\Discord.Net.Commands\Discord.Net.Commands.xproj", "{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}" | |||
EndProject | |||
Global | |||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | |||
Debug|Any CPU = Debug|Any CPU | |||
@@ -15,6 +17,10 @@ Global | |||
{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||
{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||
{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Release|Any CPU.Build.0 = Release|Any CPU | |||
{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||
{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||
{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||
{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Release|Any CPU.Build.0 = Release|Any CPU | |||
EndGlobalSection | |||
GlobalSection(SolutionProperties) = preSolution | |||
HideSolutionNode = FALSE | |||
@@ -0,0 +1,14 @@ | |||
using System; | |||
namespace Discord.Commands | |||
{ | |||
[AttributeUsage(AttributeTargets.Method)] | |||
public class CommandAttribute : Attribute | |||
{ | |||
public string Name { get; } | |||
public CommandAttribute(string name) | |||
{ | |||
Name = name; | |||
} | |||
} | |||
} |
@@ -0,0 +1,14 @@ | |||
using System; | |||
namespace Discord.Commands | |||
{ | |||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Parameter)] | |||
public class DescriptionAttribute : Attribute | |||
{ | |||
public string Text { get; } | |||
public DescriptionAttribute(string text) | |||
{ | |||
Text = text; | |||
} | |||
} | |||
} |
@@ -0,0 +1,14 @@ | |||
using System; | |||
namespace Discord.Commands | |||
{ | |||
[AttributeUsage(AttributeTargets.Class)] | |||
public class GroupAttribute : Attribute | |||
{ | |||
public string Name { get; } | |||
public GroupAttribute(string name) | |||
{ | |||
Name = name; | |||
} | |||
} | |||
} |
@@ -0,0 +1,9 @@ | |||
using System; | |||
namespace Discord.Commands | |||
{ | |||
[AttributeUsage(AttributeTargets.Class)] | |||
public class ModuleAttribute : Attribute | |||
{ | |||
} | |||
} |
@@ -0,0 +1,9 @@ | |||
using System; | |||
namespace Discord.Commands | |||
{ | |||
[AttributeUsage(AttributeTargets.Parameter)] | |||
public class UnparsedAttribute : Attribute | |||
{ | |||
} | |||
} |
@@ -0,0 +1,106 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Reflection; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
namespace Discord.Commands | |||
{ | |||
public class Module | |||
{ | |||
public string Name { get; } | |||
public IEnumerable<Command> Commands { get; } | |||
internal Module(object module, TypeInfo typeInfo) | |||
{ | |||
List<Command> commands = new List<Command>(); | |||
SearchClass(commands); | |||
Commands = commands; | |||
} | |||
private void SearchClass(List<Command> commands) | |||
{ | |||
//TODO: Implement | |||
} | |||
} | |||
public class Command | |||
{ | |||
public string SourceName { get; } | |||
} | |||
public class CommandParser | |||
{ | |||
private readonly SemaphoreSlim _moduleLock; | |||
private readonly Dictionary<object, Module> _modules; | |||
public CommandParser() | |||
{ | |||
_modules = new Dictionary<object, Module>(); | |||
_moduleLock = new SemaphoreSlim(1, 1); | |||
} | |||
public async Task<Module> Load(object module) | |||
{ | |||
await _moduleLock.WaitAsync().ConfigureAwait(false); | |||
try | |||
{ | |||
if (_modules.ContainsKey(module)) | |||
throw new ArgumentException($"This module has already been loaded."); | |||
return LoadInternal(module, module.GetType().GetTypeInfo()); | |||
} | |||
finally | |||
{ | |||
_moduleLock.Release(); | |||
} | |||
} | |||
private Module LoadInternal(object module, TypeInfo typeInfo) | |||
{ | |||
var loadedModule = new Module(module, typeInfo); | |||
_modules[module] = loadedModule; | |||
return loadedModule; | |||
} | |||
public async Task<IEnumerable<Module>> LoadAssembly(Assembly assembly) | |||
{ | |||
List<Module> modules = new List<Module>(); | |||
await _moduleLock.WaitAsync().ConfigureAwait(false); | |||
try | |||
{ | |||
foreach (var type in assembly.ExportedTypes) | |||
{ | |||
var typeInfo = type.GetTypeInfo(); | |||
if (typeInfo.GetCustomAttribute<ModuleAttribute>() != null) | |||
{ | |||
var constructor = typeInfo.DeclaredConstructors.Where(x => x.GetParameters().Length == 0).FirstOrDefault(); | |||
if (constructor == null) | |||
throw new InvalidOperationException($"Failed to find a valid constructor for \"{typeInfo.FullName}\""); | |||
object module; | |||
try { module = constructor.Invoke(null); } | |||
catch (Exception ex) | |||
{ | |||
throw new InvalidOperationException($"Failed to create \"{typeInfo.FullName}\"", ex); | |||
} | |||
modules.Add(LoadInternal(module, typeInfo)); | |||
} | |||
} | |||
return modules; | |||
} | |||
finally | |||
{ | |||
_moduleLock.Release(); | |||
} | |||
} | |||
public async Task<bool> Unload(object module) | |||
{ | |||
await _moduleLock.WaitAsync().ConfigureAwait(false); | |||
try | |||
{ | |||
return _modules.Remove(module); | |||
} | |||
finally | |||
{ | |||
_moduleLock.Release(); | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,19 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||
<PropertyGroup> | |||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion> | |||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> | |||
</PropertyGroup> | |||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" /> | |||
<PropertyGroup Label="Globals"> | |||
<ProjectGuid>078dd7e6-943d-4d09-afc2-d2ba58b76c9c</ProjectGuid> | |||
<RootNamespace>Discord.Commands</RootNamespace> | |||
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath> | |||
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath> | |||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion> | |||
</PropertyGroup> | |||
<PropertyGroup> | |||
<SchemaVersion>2.0</SchemaVersion> | |||
</PropertyGroup> | |||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" /> | |||
</Project> |
@@ -0,0 +1,34 @@ | |||
{ | |||
"version": "1.0.0-dev", | |||
"description": "A Discord.Net extension adding command support.", | |||
"authors": [ "RogueException" ], | |||
"packOptions": { | |||
"tags": [ "discord", "discordapp" ], | |||
"licenseUrl": "http://opensource.org/licenses/MIT", | |||
"projectUrl": "https://github.com/RogueException/Discord.Net", | |||
"repository": { | |||
"type": "git", | |||
"url": "git://github.com/RogueException/Discord.Net" | |||
} | |||
}, | |||
"buildOptions": { | |||
"allowUnsafe": true, | |||
"warningsAsErrors": false | |||
}, | |||
"dependencies": { | |||
"Discord.Net": "1.0.0-dev" | |||
}, | |||
"frameworks": { | |||
"netstandard1.3": { | |||
"imports": [ | |||
"dotnet5.4", | |||
"dnxcore50", | |||
"portable-net45+win8" | |||
] | |||
} | |||
} | |||
} |