Skip to content

Commit 4c6be03

Browse files
committed
Fixed missing type func conversion for cmdsys
1 parent 4913faf commit 4c6be03

File tree

5 files changed

+218
-63
lines changed

5 files changed

+218
-63
lines changed

TS3AudioBot/CommandSystem/CommandSystemTypes.cs

+3-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88

99
using System;
1010
using System.Collections.Generic;
11+
using System.Linq;
1112
using TS3AudioBot.CommandSystem.CommandResults;
13+
using TSLib;
1214

1315
namespace TS3AudioBot.CommandSystem
1416
{
@@ -35,6 +37,6 @@ public static class CommandSystemTypes
3537
typeof(ResourceFactories.AudioResource),
3638
typeof(History.AudioLogEntry),
3739
typeof(Playlists.PlaylistItem),
38-
});
40+
}.Concat(TsTypes.All));
3941
}
4042
}

TS3AudioBot/CommandSystem/Commands/FunctionCommand.cs

+74-11
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
using System.Globalization;
1515
using System.Linq;
1616
using System.Reflection;
17+
using System.Reflection.Emit;
1718
using System.Threading.Tasks;
1819
using TS3AudioBot.CommandSystem.CommandResults;
1920
using TS3AudioBot.Dependency;
@@ -22,6 +23,7 @@
2223
using TS3AudioBot.Web.Api;
2324
using TSLib.Helper;
2425
using static TS3AudioBot.CommandSystem.CommandSystemTypes;
26+
using TryFromFn = System.Func<object?, object?>;
2527

2628
namespace TS3AudioBot.CommandSystem.Commands
2729
{
@@ -158,7 +160,7 @@ public FunctionCommand(Func<string, string> command) : this(command.Method, comm
158160
if (arg.Kind == ParamKind.NormalTailString && argResultP is TailString tailString)
159161
parameters[p] = tailString.Tail;
160162
else
161-
parameters[p] = ConvertParam(argResultP, argType, filterLazy);
163+
parameters[p] = ConvertParam(argResultP, argType, arg, filterLazy);
162164

163165
takenArguments++;
164166
break;
@@ -171,7 +173,7 @@ public FunctionCommand(Func<string, string> command) : this(command.Method, comm
171173
for (int i = 0; i < args.Length; i++, takenArguments++)
172174
{
173175
var argResultA = await arguments[takenArguments].Execute(info, Array.Empty<ICommand>());
174-
var convResult = ConvertParam(argResultA, typeArr, filterLazy);
176+
var convResult = ConvertParam(argResultA, typeArr, arg, filterLazy);
175177
args.SetValue(convResult, i);
176178
}
177179

@@ -222,14 +224,63 @@ private static ParamInfo[] PrecomputeTypes(ParameterInfo[] parameterInfos)
222224
kind = ParamKind.NormalArray;
223225
else if (arg.IsEnum
224226
|| BasicTypes.Contains(arg)
225-
|| BasicTypes.Contains(UnwrapParamType(arg)))
227+
|| BasicTypes.Contains(UnwrapParamType(arg))
228+
|| AdvancedTypes.Contains(arg)
229+
|| AdvancedTypes.Contains(UnwrapParamType(arg)))
226230
kind = ParamKind.NormalParam;
227231
// TODO How to distinguish between special type and dependency?
228-
else if (AdvancedTypes.Contains(arg))
229-
kind = ParamKind.NormalParam;
230232
else
231233
kind = ParamKind.Dependency;
232234

235+
TryFromFn? tryFrom = null;
236+
if (kind == ParamKind.NormalParam || kind == ParamKind.NormalArray)
237+
{
238+
var finalType = arg;
239+
if (kind == ParamKind.NormalArray)
240+
finalType = finalType.GetElementType() ?? throw new InvalidOperationException("Not an array?");
241+
finalType = UnwrapParamType(finalType);
242+
var method = finalType.GetMethod("TryFrom", BindingFlags.Public | BindingFlags.Static);
243+
if (method != null)
244+
{
245+
if (method.ReturnType == typeof(object))
246+
{
247+
// return directly: (object) -> object
248+
tryFrom = (TryFromFn)method.CreateDelegate(typeof(TryFromFn));
249+
}
250+
else if (method.ReturnType.IsClass)
251+
{
252+
// have: [object]->class
253+
// can be casted with covariance to [object]->object
254+
tryFrom = (TryFromFn)method.CreateDelegate(typeof(Func<,>).MakeGenericType(typeof(object), method.ReturnType));
255+
}
256+
else if(method.ReturnType.IsValueType)
257+
{
258+
static TryFromFn? TryCreateWith(MethodInfo method, Type retType)
259+
{
260+
if (method.ReturnType != retType) return null;
261+
try
262+
{
263+
var tryFromToObjectWrapper = new DynamicMethod(
264+
"TryFromToObjectWrapper", typeof(object), new[] { typeof(object) }, typeof(FunctionCommand).Module);
265+
var il = tryFromToObjectWrapper.GetILGenerator();
266+
il.Emit(OpCodes.Ldarg_0);
267+
il.Emit(OpCodes.Call, method);
268+
il.Emit(OpCodes.Box, retType);
269+
il.Emit(OpCodes.Ret);
270+
return (TryFromFn)tryFromToObjectWrapper.CreateDelegate(typeof(TryFromFn));
271+
}
272+
catch { return null; }
273+
}
274+
// have: [object]->value
275+
// box with (object)([object]->value)
276+
tryFrom ??= TryCreateWith(method, method.ReturnType);
277+
// have: [object]->value?
278+
// box with (object)([object]->value?)
279+
tryFrom ??= TryCreateWith(method, typeof(Nullable<>).MakeGenericType(method.ReturnType));
280+
}
281+
}
282+
}
283+
233284
if (kind.IsNormal())
234285
{
235286
// If we have the last normal parameter, check if it fits the criteria
@@ -244,7 +295,8 @@ private static ParamInfo[] PrecomputeTypes(ParameterInfo[] parameterInfos)
244295
precomputed[i] = new ParamInfo(
245296
parameterInfo,
246297
kind,
247-
parameterInfo.IsOptional || parameterInfo.GetCustomAttribute<ParamArrayAttribute>() != null);
298+
parameterInfo.IsOptional || parameterInfo.GetCustomAttribute<ParamArrayAttribute>() != null,
299+
tryFrom);
248300
}
249301

250302
return precomputed;
@@ -307,7 +359,7 @@ public static CommandException ThrowAtLeastNArguments(int count)
307359
}
308360

309361
[return: NotNullIfNotNull("value")]
310-
public static object? ConvertParam(object? value, Type targetType, Lazy<Algorithm.IFilter> filter)
362+
public static object? ConvertParam(object? value, Type targetType, ParamInfo param, Lazy<Algorithm.IFilter> filter)
311363
{
312364
if (value is null)
313365
return null;
@@ -322,6 +374,15 @@ public static CommandException ThrowAtLeastNArguments(int count)
322374
if (targetType == valueType || targetType.IsAssignableFrom(valueType))
323375
return value;
324376

377+
if (param.TryFrom != null)
378+
{
379+
var tryResult = param.TryFrom(value);
380+
if (tryResult != null)
381+
{
382+
return tryResult;
383+
}
384+
}
385+
325386
if (targetType.IsEnum)
326387
{
327388
var strValue = value.ToString() ?? throw new ArgumentNullException(nameof(value));
@@ -374,19 +435,21 @@ public enum ParamKind
374435
}
375436

376437
[DebuggerDisplay("{Kind} {Name,nq}{Optional ? \"?\" : \"\",nq} ({Type.Name,nq})")]
377-
public readonly struct ParamInfo
438+
public class ParamInfo
378439
{
379440
public ParamKind Kind { get; }
380441
public bool Optional { get; }
381442
public ParameterInfo Param { get; }
382-
public readonly Type Type => Param.ParameterType;
383-
public readonly string Name => Param.Name ?? "<no name>";
443+
public TryFromFn? TryFrom { get; }
444+
public Type Type => Param.ParameterType;
445+
public string Name => Param.Name ?? "<no name>";
384446

385-
public ParamInfo(ParameterInfo param, ParamKind kind, bool optional)
447+
public ParamInfo(ParameterInfo param, ParamKind kind, bool optional, TryFromFn? tryFrom)
386448
{
387449
Param = param;
388450
Kind = kind;
389451
Optional = optional;
452+
TryFrom = tryFrom;
390453
}
391454
}
392455

TS3AudioBot/MainCommands.cs

+14-13
Original file line numberDiff line numberDiff line change
@@ -1597,14 +1597,13 @@ public static void CommandSubscribeTempChannel(IVoiceTarget targetManager, Clien
15971597
}
15981598

15991599
[Command("subscribe channel")]
1600-
public static void CommandSubscribeChannel(IVoiceTarget targetManager, ClientCall? invoker = null, params ulong[] channels_ids)
1600+
public static void CommandSubscribeChannel(IVoiceTarget targetManager, ClientCall? invoker = null, params ChannelId[] channels)
16011601
{
1602-
var channels = Array.ConvertAll(channels_ids, item => (ChannelId)item);
1603-
1604-
if (channels == null || channels.Length == 0)
1602+
if (channels.Length == 0)
16051603
{
1606-
var subChan = invoker?.ChannelId ?? ChannelId.Null;
1607-
targetManager.WhisperChannelSubscribe(false, subChan);
1604+
var subChan = invoker?.ChannelId;
1605+
if (subChan.HasValue)
1606+
targetManager.WhisperChannelSubscribe(false, subChan.Value);
16081607
}
16091608
else targetManager.WhisperChannelSubscribe(false, channels);
16101609
}
@@ -1705,16 +1704,18 @@ public static void CommandUnsubscribe(IVoiceTarget targetManager, ClientCall inv
17051704
}
17061705

17071706
[Command("unsubscribe channel")]
1708-
public static void CommandUnsubscribeChannel(IVoiceTarget targetManager, ClientCall? invoker = null, params ulong[] channels_ids)
1707+
public static void CommandUnsubscribeChannel(IVoiceTarget targetManager, ClientCall? invoker = null, params ChannelId[] channels)
17091708
{
1710-
var channels = Array.ConvertAll(channels_ids, item => (ChannelId)item);
1711-
1712-
if (channels == null || channels.Length == 0)
1709+
if (channels.Length == 0)
1710+
{
1711+
var subChan = invoker?.ChannelId;
1712+
if (subChan.HasValue)
1713+
targetManager.WhisperChannelUnsubscribe(false, subChan.Value);
1714+
}
1715+
else
17131716
{
1714-
var subChan = invoker?.ChannelId ?? ChannelId.Null;
1715-
targetManager.WhisperChannelUnsubscribe(false, subChan);
1717+
targetManager.WhisperChannelUnsubscribe(false, channels);
17161718
}
1717-
else targetManager.WhisperChannelUnsubscribe(false, channels);
17181719
}
17191720

17201721
[Command("unsubscribe temporary")]

0 commit comments

Comments
 (0)