/*
* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Plugins.Runtime
* File: LivePlugin.cs
*
* LivePlugin.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.Linq;
using System.Reflection;
using VNLib.Plugins.Attributes;
namespace VNLib.Plugins.Runtime
{
///
///
/// Wrapper for a loaded instance, used internally
/// for a single instance.
///
///
/// Lifetime: for the existance of a single loaded
/// plugin instance. Created once per loaded plugin instance. Once the plugin
/// is unloaded, it is no longer useable.
///
///
public class LivePlugin : IEquatable, IEquatable
{
private bool _loaded;
///
/// The plugin's property during load time
///
///
public string PluginName => Plugin?.PluginName ?? throw new InvalidOperationException("Plugin is not loaded");
///
/// The underlying that is warpped
/// by he current instance
///
public IPlugin? Plugin { get; private set; }
///
/// The assembly that this plugin was created from
///
public Assembly OriginAsm { get; }
///
/// The exposed runtime type of the plugin. Equivalent to
/// calling Plugin.GetType()
///
public Type PluginType { get; }
private ConsoleEventHandlerSignature? PluginConsoleHandler;
internal LivePlugin(IPlugin plugin, Assembly originAsm)
{
Plugin = plugin;
OriginAsm = originAsm;
PluginType = plugin.GetType();
GetConsoleHandler();
}
private void GetConsoleHandler()
{
//Get the console handler method from the plugin instance
MethodInfo? handler = (from m in PluginType.GetMethods()
where m.GetCustomAttribute() != null
select m)
.FirstOrDefault();
//Get a delegate handler for the plugin
PluginConsoleHandler = handler?.CreateDelegate(Plugin);
}
///
/// Sets the plugin's configuration if it defines a
/// on an instance method
///
/// The host configuration DOM
internal void InitConfig(ReadOnlySpan configData)
{
//Get the console handler method from the plugin instance
MethodInfo? confHan = PluginType.GetMethods().Where(static m => m.GetCustomAttribute() != null)
.FirstOrDefault();
//Get a delegate handler for the plugin
ConfigInitializer? configInit = confHan?.CreateDelegate(Plugin);
if (configInit == null)
{
return;
}
//Invoke
configInit.Invoke(configData);
}
///
/// Invokes the plugin's log initalizer method if it defines a
/// on an instance method
///
/// The current process's CLI args
internal void InitLog(string[] cliArgs)
{
//Get the console handler method from the plugin instance
MethodInfo? logInit = (from m in PluginType.GetMethods()
where m.GetCustomAttribute() != null
select m)
.FirstOrDefault();
//Get a delegate handler for the plugin
LogInitializer? logFunc = logInit?.CreateDelegate(Plugin);
//Invoke
logFunc?.Invoke(cliArgs);
}
///
/// Gets services from the plugin if it is loaded and
/// publishes them to the pool
///
/// The service pool to collect services into
///
internal void GetServices(IPluginServicePool pool)
{
if (!_loaded)
{
throw new InvalidOperationException("Plugin is not loaded");
}
//Load services into pool
Plugin?.PublishServices(pool);
}
///
/// Invokes the plugins console event handler if the type has one
/// and the plugin is loaded.
///
/// The message to pass to the plugin handler
///
/// True if the command was sent to the plugin, false if the plugin is
/// unloaded or did not export a console event handler
///
public bool SendConsoleMessage(string message)
{
//Make sure plugin is loaded and has a console handler
if (PluginConsoleHandler == null)
{
return false;
}
//Invoke plugin console handler
PluginConsoleHandler(message);
return true;
}
///
/// Calls the method on the plugin if its loaded
///
internal void LoadPlugin()
{
//Load and set loaded flag
Plugin?.Load();
_loaded = true;
}
///
/// Unloads the plugin, only if the plugin was successfully loaded by
/// calling the event hook.
///
internal void UnloadPlugin()
{
//Remove delegate handler to the plugin to remove refs
PluginConsoleHandler = null;
//Only call unload if the plugin successfully loaded
if (!_loaded)
{
return;
}
try
{
Plugin?.Unload();
}
finally
{
Plugin = null;
}
}
///
public override bool Equals(object? obj)
{
Type? pluginType = Plugin?.GetType();
Type? otherType = obj?.GetType();
if(pluginType == null || otherType == null)
{
return false;
}
//If the other plugin is the same type as the current instance return true
return pluginType.FullName == otherType.FullName;
}
///
public bool Equals(LivePlugin? other)
{
return Equals(other?.Plugin);
}
///
public bool Equals(IPlugin? other)
{
return Equals((object?)other);
}
///
public override int GetHashCode()
{
return Plugin?.GetHashCode() ?? throw new InvalidOperationException("Plugin is null");
}
}
}