Files
OpenNest/OpenNest.Data/EngineSelectionSettings.cs
T
ajandClaude Opus 5.5 b550640ef0 refactor: merge duplicated geometry, settings-file and form helpers
Roslyn Bridge flagged these as copy-paste clusters; each pair now shares
one implementation so a fix lands everywhere at once:

- Shape.OffsetInward/OffsetOutward differed only in the target winding.
- BestFitFinder kept its own copy of SpatialQuery's arc-extreme points.
- SplitDrawingForm rebuilt DrawingSplitter's clip-region grid for preview.
- GeometryOptimizer's collinear/coradial finders (one misspelled
  GetCoradialArs) become one generic FindRelated.
- Five settings files repeated the write-with-retry loop, and two also
  repeated the unit parser and unreadable-file filter (SettingsFile).
- ClampsForm and SupportGridForm repeated their row/box builders.
- Vertical/HorizontalRemnantComparer share a RemnantComparer base.

Behavior is unchanged except that Engine/NestStorage settings loads now
also tolerate NotSupported/Security exceptions, matching the other
settings files' never-throw contract.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 12:59:47 -04:00

79 lines
3.2 KiB
C#

using System.Text.Json;
namespace OpenNest.Data;
/// <summary>
/// Last-used Auto Nest engine, stored separately from nest/plate defaults.
/// Loading does not resolve the name: the host must finish plug-in discovery first.
/// </summary>
public sealed class EngineSelectionSettings
{
public const string DefaultEngineName = "Default";
private static readonly JsonSerializerOptions JsonOptions = new()
{
WriteIndented = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
PropertyNameCaseInsensitive = true,
};
public string EngineName { get; set; } = DefaultEngineName;
/// <summary>%APPDATA%\OpenNest\engine-selection.json.</summary>
public static string DefaultPath => Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"OpenNest", "engine-selection.json");
/// <summary>Missing, unreadable or corrupt settings safely use Default without writing.</summary>
public static EngineSelectionSettings Load(string path)
{
var settings = SettingsFile.Read<EngineSelectionSettings>(path, JsonOptions) ?? new();
settings.EngineName = NormalizeName(settings.EngineName);
return settings;
}
/// <summary>
/// Resolves against the host's selectable engines AFTER plug-in loading. A missing engine
/// returns Default plus a status-bar warning, without replacing the saved preference.
/// Names use registry casing so desktop combo-box selection remains exact. When the saved
/// name is not selectable, <paramref name="renamed"/> (the registry's legacy-name lookup)
/// may map it to the engine that replaced it; the result must itself be selectable.
/// </summary>
public string Resolve(
IEnumerable<string> availableEngineNames,
out string? statusMessage,
Func<string, string?>? renamed = null)
{
ArgumentNullException.ThrowIfNull(availableEngineNames);
var available = availableEngineNames.ToList();
var requestedName = NormalizeName(EngineName);
var registeredName = Find(available, requestedName)
?? (renamed?.Invoke(requestedName) is { } replacement ? Find(available, replacement) : null);
if (registeredName is not null)
{
statusMessage = null;
return registeredName;
}
statusMessage = $"Saved Auto Nest engine '{requestedName}' is unavailable. Using Default.";
return DefaultEngineName;
}
private static string? Find(IEnumerable<string> names, string name) =>
names.FirstOrDefault(n => string.Equals(n, name, StringComparison.OrdinalIgnoreCase));
/// <summary>
/// Writes camelCase JSON, creating the parent directory and retrying IO collisions as
/// LocalJsonProvider does. The host handles a persistent write failure.
/// </summary>
public void Save(string path)
{
var json = JsonSerializer.Serialize(
new EngineSelectionSettings { EngineName = NormalizeName(EngineName) }, JsonOptions);
SettingsFile.Write(path, json);
}
private static string NormalizeName(string? name) =>
string.IsNullOrWhiteSpace(name) ? DefaultEngineName : name.Trim();
}