From 46fe48870ce892600f5ac1389d644e678f5cc1e5 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Wed, 18 Mar 2026 12:57:31 -0400 Subject: [PATCH] feat(engine): add FillStrategyRegistry with reflection-based discovery Co-Authored-By: Claude Sonnet 4.6 --- .../Strategies/FillStrategyRegistry.cs | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 OpenNest.Engine/Strategies/FillStrategyRegistry.cs diff --git a/OpenNest.Engine/Strategies/FillStrategyRegistry.cs b/OpenNest.Engine/Strategies/FillStrategyRegistry.cs new file mode 100644 index 0000000..f146cdf --- /dev/null +++ b/OpenNest.Engine/Strategies/FillStrategyRegistry.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; + +namespace OpenNest +{ + public static class FillStrategyRegistry + { + private static readonly List strategies = new(); + private static List sorted; + + static FillStrategyRegistry() + { + LoadFrom(typeof(FillStrategyRegistry).Assembly); + } + + public static IReadOnlyList Strategies => + sorted ??= strategies.OrderBy(s => s.Order).ToList(); + + public static void LoadFrom(Assembly assembly) + { + foreach (var type in assembly.GetTypes()) + { + if (type.IsAbstract || type.IsInterface || !typeof(IFillStrategy).IsAssignableFrom(type)) + continue; + + var ctor = type.GetConstructor(Type.EmptyTypes); + if (ctor == null) + { + Debug.WriteLine($"[FillStrategyRegistry] Skipping {type.Name}: no parameterless constructor"); + continue; + } + + try + { + var instance = (IFillStrategy)ctor.Invoke(null); + + if (strategies.Any(s => s.Name.Equals(instance.Name, StringComparison.OrdinalIgnoreCase))) + { + Debug.WriteLine($"[FillStrategyRegistry] Duplicate strategy '{instance.Name}' skipped"); + continue; + } + + strategies.Add(instance); + Debug.WriteLine($"[FillStrategyRegistry] Registered: {instance.Name} (Order={instance.Order})"); + } + catch (Exception ex) + { + Debug.WriteLine($"[FillStrategyRegistry] Failed to instantiate {type.Name}: {ex.Message}"); + } + } + + sorted = null; + } + + public static void LoadPlugins(string directory) + { + if (!Directory.Exists(directory)) + return; + + foreach (var dll in Directory.GetFiles(directory, "*.dll")) + { + try + { + var assembly = Assembly.LoadFrom(dll); + LoadFrom(assembly); + Debug.WriteLine($"[FillStrategyRegistry] Loaded plugin assembly: {Path.GetFileName(dll)}"); + } + catch (Exception ex) + { + Debug.WriteLine($"[FillStrategyRegistry] Failed to load {Path.GetFileName(dll)}: {ex.Message}"); + } + } + } + } +}