Restructure solution layout by module

This commit is contained in:
master
2025-10-28 15:10:40 +02:00
parent 95daa159c4
commit d870da18ce
4103 changed files with 192899 additions and 187024 deletions

View File

@@ -0,0 +1,172 @@
using StellaOps.Plugin.Hosting;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Linq;
using System.Threading.Tasks;
namespace StellaOps.Plugin;
public interface IAvailabilityPlugin
{
string Name { get; }
bool IsAvailable(IServiceProvider services);
}
public interface IFeedConnector
{
string SourceName { get; }
Task FetchAsync(IServiceProvider services, CancellationToken cancellationToken);
Task ParseAsync(IServiceProvider services, CancellationToken cancellationToken);
Task MapAsync(IServiceProvider services, CancellationToken cancellationToken);
}
public interface IFeedExporter
{
string Name { get; }
Task ExportAsync(IServiceProvider services, CancellationToken cancellationToken);
}
public interface IConnectorPlugin : IAvailabilityPlugin
{
IFeedConnector Create(IServiceProvider services);
}
public interface IExporterPlugin : IAvailabilityPlugin
{
IFeedExporter Create(IServiceProvider services);
}
public sealed class PluginCatalog
{
private readonly List<Assembly> _assemblies = new();
private readonly HashSet<string> _assemblyLocations = new(StringComparer.OrdinalIgnoreCase);
public PluginCatalog AddAssembly(Assembly assembly)
{
if (assembly == null) throw new ArgumentNullException(nameof(assembly));
if (_assemblies.Contains(assembly))
{
return this;
}
_assemblies.Add(assembly);
if (!string.IsNullOrWhiteSpace(assembly.Location))
{
_assemblyLocations.Add(Path.GetFullPath(assembly.Location));
}
return this;
}
public PluginCatalog AddFromDirectory(string directory, string searchPattern = "StellaOps.Concelier.*.dll")
{
if (string.IsNullOrWhiteSpace(directory)) throw new ArgumentException("Directory is required", nameof(directory));
var fullDirectory = Path.GetFullPath(directory);
var options = new PluginHostOptions
{
PluginsDirectory = fullDirectory,
EnsureDirectoryExists = false,
RecursiveSearch = false,
};
options.SearchPatterns.Add(searchPattern);
var result = PluginHost.LoadPlugins(options);
foreach (var plugin in result.Plugins)
{
AddAssembly(plugin.Assembly);
}
return this;
}
public IReadOnlyList<IConnectorPlugin> GetConnectorPlugins() => PluginLoader.LoadPlugins<IConnectorPlugin>(_assemblies);
public IReadOnlyList<IExporterPlugin> GetExporterPlugins() => PluginLoader.LoadPlugins<IExporterPlugin>(_assemblies);
public IReadOnlyList<IConnectorPlugin> GetAvailableConnectorPlugins(IServiceProvider services)
=> FilterAvailable(GetConnectorPlugins(), services);
public IReadOnlyList<IExporterPlugin> GetAvailableExporterPlugins(IServiceProvider services)
=> FilterAvailable(GetExporterPlugins(), services);
private static IReadOnlyList<TPlugin> FilterAvailable<TPlugin>(IEnumerable<TPlugin> plugins, IServiceProvider services)
where TPlugin : IAvailabilityPlugin
{
var list = new List<TPlugin>();
foreach (var plugin in plugins)
{
try
{
if (plugin.IsAvailable(services))
{
list.Add(plugin);
}
}
catch
{
// Treat exceptions as plugin not available.
}
}
return list;
}
}
public static class PluginLoader
{
public static IReadOnlyList<TPlugin> LoadPlugins<TPlugin>(IEnumerable<Assembly> assemblies)
where TPlugin : class
{
if (assemblies == null) throw new ArgumentNullException(nameof(assemblies));
var plugins = new List<TPlugin>();
var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var assembly in assemblies)
{
foreach (var candidate in SafeGetTypes(assembly))
{
if (candidate.IsAbstract || candidate.IsInterface)
{
continue;
}
if (!typeof(TPlugin).IsAssignableFrom(candidate))
{
continue;
}
if (Activator.CreateInstance(candidate) is not TPlugin plugin)
{
continue;
}
var key = candidate.FullName ?? candidate.Name;
if (key is null || !seen.Add(key))
{
continue;
}
plugins.Add(plugin);
}
}
return plugins;
}
private static IEnumerable<Type> SafeGetTypes(Assembly assembly)
{
try
{
return assembly.GetTypes();
}
catch (ReflectionTypeLoadException ex)
{
return ex.Types.Where(t => t is not null)!;
}
}
}