using System;
using System.Linq;
using System.Text.Json;
using System.Reflection;
using System.Collections.Generic;
using VNLib.Plugins.Extensions.Loading.Events;
using VNLib.Plugins.Extensions.Loading.Configuration;
namespace VNLib.Plugins.Extensions.Loading.Routing
{
///
/// Provides advanced QOL features to plugin loading
///
public static class RoutingExtensions
{
///
/// Constructs and routes the specific endpoint type for the current plugin
///
/// The type
///
/// The path to the plugin sepcific configuration property
///
public static T Route(this PluginBase plugin, string? pluginConfigPathName) where T : IEndpoint
{
Type endpointType = typeof(T);
try
{
//If the config attribute is not set, then ignore the config variables
if (string.IsNullOrWhiteSpace(pluginConfigPathName))
{
ConstructorInfo? constructor = endpointType.GetConstructor(new Type[] { typeof(PluginBase) });
_ = constructor ?? throw new EntryPointNotFoundException($"No constructor found for {endpointType.Name}");
//Create the new endpoint and pass the plugin instance
T endpoint = (T)constructor.Invoke(new object[] { plugin });
//Register event handlers for the endpoint
ScheduleIntervals(plugin, endpoint, endpointType, null);
//Route the endpoint
plugin.Route(endpoint);
return endpoint;
}
else
{
ConstructorInfo? constructor = endpointType.GetConstructor(new Type[] { typeof(PluginBase), typeof(Dictionary) });
//Make sure the constructor exists
_ = constructor ?? throw new EntryPointNotFoundException($"No constructor found for {endpointType.Name}");
//Get config variables for the endpoint
IReadOnlyDictionary conf = plugin.GetConfig(pluginConfigPathName);
//Create the new endpoint and pass the plugin instance along with the configuration object
T endpoint = (T)constructor.Invoke(new object[] { plugin, conf });
//Register event handlers for the endpoint
ScheduleIntervals(plugin, endpoint, endpointType, conf);
//Route the endpoint
plugin.Route(endpoint);
return endpoint;
}
}
catch (TargetInvocationException te) when (te.InnerException != null)
{
throw te.InnerException;
}
}
///
/// Constructs and routes the specific endpoint type for the current plugin
///
/// The type
///
///
public static T Route(this PluginBase plugin) where T : IEndpoint
{
Type endpointType = typeof(T);
//Get config name attribute
ConfigurationNameAttribute? configAttr = endpointType.GetCustomAttribute();
//Route using attribute
return plugin.Route(configAttr?.ConfigVarName);
}
private static void ScheduleIntervals(PluginBase plugin, T endpointInstance, Type epType, IReadOnlyDictionary? endpointLocalConfig) where T: IEndpoint
{
List registered = new();
try
{
//Get all methods that have the configureable async interval attribute specified
IEnumerable> confIntervals = epType.GetMethods()
.Where(m => m.GetCustomAttribute() != null)
.Select(m => new Tuple
(m.GetCustomAttribute()!, m.CreateDelegate(endpointInstance)));
//If the endpoint has a local config, then use it to find the interval
if (endpointLocalConfig != null)
{
//Schedule event handlers on the current plugin
foreach (Tuple interval in confIntervals)
{
int value = endpointLocalConfig[interval.Item1.IntervalPropertyName].GetInt32();
//Get the timeout from its resolution variable
TimeSpan timeout = interval.Item1.Resolution switch
{
IntervalResultionType.Seconds => TimeSpan.FromSeconds(value),
IntervalResultionType.Minutes => TimeSpan.FromMinutes(value),
IntervalResultionType.Hours => TimeSpan.FromHours(value),
_ => TimeSpan.FromMilliseconds(value),
};
//Schedule
registered.Add(plugin.ScheduleInterval(interval.Item2, timeout));
}
}
//Get all methods that have the async interval attribute specified
IEnumerable> intervals = epType.GetMethods()
.Where(m => m.GetCustomAttribute() != null)
.Select(m => new Tuple(
m.GetCustomAttribute()!, m.CreateDelegate(endpointInstance))
);
//Schedule event handlers on the current plugin
foreach (Tuple interval in intervals)
{
registered.Add(plugin.ScheduleInterval(interval.Item2, interval.Item1.Interval));
}
}
catch
{
//Stop all event handles
foreach(EventHandle evh in registered)
{
evh.Dispose();
}
throw;
}
}
}
}