/*
* Copyright (c) 2023 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Plugins.Runtime
* File: LoaderExtensions.cs
*
* LoaderExtensions.cs is part of VNLib.Plugins.Runtime which is part of the larger
* VNLib collection of libraries and utilities.
*
* VNLib.Plugins.Runtime is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 2 of the License,
* or (at your option) any later version.
*
* VNLib.Plugins.Runtime is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with VNLib.Plugins.Runtime. If not, see http://www.gnu.org/licenses/.
*/
using System;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
namespace VNLib.Plugins.Runtime
{
///
/// Contains extension methods for PluginLoader library
///
public static class LoaderExtensions
{
/*
* Class that manages a collection registration for a specific type
* dependency, and redirects the event calls for the consumed service
*/
private sealed class TypedRegistration : IPluginEventListener where T: class
{
private readonly ITypedPluginConsumer _consumerEvents;
private readonly object? _userState;
private T? _service;
private readonly Type _type;
public TypedRegistration(ITypedPluginConsumer consumerEvents, Type type)
{
_consumerEvents = consumerEvents;
_type = type;
}
public void OnPluginLoaded(PluginController controller, object? state)
{
//Get the service from the loaded plugins
T service = controller.Plugins
.Where(pl => _type.IsAssignableFrom(pl.PluginType))
.Select(static pl => (T)pl.Plugin!)
.First();
//Call load with the exported type
_consumerEvents.OnLoad(service, _userState);
//Store for unload
_service = service;
}
public void OnPluginUnloaded(PluginController controller, object? state)
{
//Unload
_consumerEvents.OnUnload(_service!, _userState);
_service = null;
}
}
///
/// Registers a plugin even handler for the current
/// for a specific type.
///
///
///
/// The typed plugin instance event consumer
/// A handle that manages this event registration
///
public static PluginEventRegistration RegisterForType(this PluginController collection, ITypedPluginConsumer consumer) where T: class
{
Type serviceType = typeof(T);
//Confim the type is exposed by this collection
if(!ExposesType(collection, serviceType))
{
throw new ArgumentException("The requested type is not exposed in this assembly");
}
//Create new typed listener
TypedRegistration reg = new(consumer, serviceType);
//register event handler
return Register(collection, reg, null);
}
///
/// Registers a handler to listen for plugin load/unload events
///
///
/// A handle that will unregister the listener when disposed
public static PluginEventRegistration Register(this IPluginEventRegistrar reg, IPluginEventListener listener, object? state = null)
{
reg.Register(listener, state);
return new(reg, listener);
}
///
/// Loads the configuration file into its format
/// for reading.
///
///
/// A new of the loaded configuration file
public static async Task GetPluginConfigAsync(this RuntimePluginLoader loader)
{
//Open and read the config file
await using FileStream confStream = File.OpenRead(loader.PluginConfigPath);
JsonDocumentOptions jdo = new()
{
AllowTrailingCommas = true,
CommentHandling = JsonCommentHandling.Skip,
};
//parse the plugin config file
return await JsonDocument.ParseAsync(confStream, jdo);
}
///
/// Determines if the current
/// exposes the desired type on is
/// type.
///
///
/// The desired type to request
/// True if the plugin exposes the desired type, false otherwise
public static bool ExposesType(this PluginController collection, Type type)
{
return collection.Plugins
.Where(pl => type.IsAssignableFrom(pl.PluginType))
.Any();
}
///
/// Searches all plugins within the current loader for a
/// single plugin that derrives the specified type
///
/// The type the plugin must derrive from
///
/// The instance of your custom type casted, or null if not found or could not be casted
public static T? GetExposedTypes(this PluginController collection) where T: class
{
LivePlugin? plugin = collection.Plugins
.Where(static pl => typeof(T).IsAssignableFrom(pl.PluginType))
.SingleOrDefault();
return plugin?.Plugin as T;
}
}
}