/* * 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; 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 JsonDocument GetPluginConfig(this RuntimePluginLoader loader) { //Open and read the config file using FileStream confStream = File.OpenRead(loader.PluginConfigPath); JsonDocumentOptions jdo = new() { AllowTrailingCommas = true, CommentHandling = JsonCommentHandling.Skip, }; //parse the plugin config file return JsonDocument.Parse(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; } } }