Add IImageCreator abstraction and runtime provider selection

Introduce IImageCreator interface for image creation, and update ImageCreatorSharp to implement it. Add ImageCreatorAlternate (adapter) and ImageCreatorMapper (runtime selector) classes. Extend PicSettings with ImageCreatorProvider to control backend selection. Update DI registrations and refactor ImageCreationStuff to depend on IImageCreator, enabling backend switching via configuration.
This commit is contained in:
MaddoScientisto 2026-02-15 00:14:04 +01:00
commit e48c0d266b
7 changed files with 92 additions and 2 deletions

View file

@ -0,0 +1,9 @@
using System.Drawing;
using System.Threading.Tasks;
namespace MaddoShared;
public interface IImageCreator
{
Task CreateImageAsync(ImageState imgState, Image logo);
}

View file

@ -18,7 +18,7 @@ namespace MaddoShared
public class ImageCreationStuff(
ILogger<ImageCreationStuff> logger,
PicSettings picSettings,
ImageCreatorSharp imageCreatorService)
IImageCreator imageCreatorService)
{
public class Options
{

View file

@ -0,0 +1,25 @@
using System.Drawing;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace MaddoShared;
// Minimal alternate adapter that currently delegates to ImageCreatorSharp.
// Later this can be replaced with a different library implementation.
public class ImageCreatorAlternate : IImageCreator
{
private readonly ImageCreatorSharp _inner;
private readonly ILogger<ImageCreatorAlternate> _logger;
public ImageCreatorAlternate(ImageCreatorSharp inner, ILogger<ImageCreatorAlternate> logger)
{
_inner = inner;
_logger = logger;
}
public Task CreateImageAsync(ImageState imgState, Image logo)
{
_logger.LogDebug("Using alternate image creator adapter");
return _inner.CreateImageAsync(imgState, logo);
}
}

View file

@ -0,0 +1,49 @@
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace MaddoShared;
/// <summary>
/// Dynamically resolves the concrete IImageCreator implementation at call time
/// based on current PicSettings.ImageCreatorProvider.
/// </summary>
public class ImageCreatorMapper : IImageCreator
{
private readonly IServiceProvider _sp;
private readonly PicSettings _settings;
private readonly ILogger<ImageCreatorMapper> _logger;
public ImageCreatorMapper(IServiceProvider sp, PicSettings settings, ILogger<ImageCreatorMapper> logger)
{
_sp = sp ?? throw new ArgumentNullException(nameof(sp));
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
_logger = logger;
}
public Task CreateImageAsync(ImageState imgState, System.Drawing.Image logo)
{
var provider = (_settings.ImageCreatorProvider ?? "Sharp").Trim();
_logger?.LogDebug("Resolving IImageCreator for provider '{Provider}'", provider);
return provider.Equals("ALTERNATE", StringComparison.OrdinalIgnoreCase)
? ResolveAndCall<ImageCreatorAlternate>(imgState, logo)
: ResolveAndCall<ImageCreatorSharp>(imgState, logo);
}
private Task ResolveAndCall<T>(ImageState imgState, System.Drawing.Image logo) where T : IImageCreator
{
// Resolve the concrete implementation and forward the call
var impl = (IImageCreator)_sp.GetService(typeof(T));
if (impl is null)
{
_logger?.LogWarning("Requested image creator {Type} is not registered. Falling back to ImageCreatorSharp.", typeof(T).Name);
impl = (IImageCreator)_sp.GetService(typeof(ImageCreatorSharp));
}
if (impl is null)
throw new InvalidOperationException("No IImageCreator implementation is registered.");
return impl.CreateImageAsync(imgState, logo);
}
}

View file

@ -15,7 +15,7 @@ using SixLabors.ImageSharp.Metadata.Profiles.Exif;
namespace MaddoShared;
[SuppressMessage("Interoperability", "CA1416:Validate platform compatibility")]
public class ImageCreatorSharp(PicSettings picSettings, ILogger<ImageCreatorSharp> logger)
public class ImageCreatorSharp(PicSettings picSettings, ILogger<ImageCreatorSharp> logger) : IImageCreator
{
public async Task CreateImageAsync(ImageState imgState, Image logo)
{

View file

@ -66,4 +66,6 @@ public class PicSettings
public bool FotoRuotaASinistra { get; set; } = false;
public string TempMinText { get; set; } = string.Empty;
public bool OverwriteFiles { get; set; } = false;
// Which image creator to use: "Sharp" for current implementation, "Alternate" for alternate library
public string ImageCreatorProvider { get; set; } = "Sharp";
}

View file

@ -103,6 +103,11 @@ static class Program
services.AddTransient<ImageCreationStuff>();
services.AddTransient<ImageCreatorSharp>();
services.AddTransient<ImageCreatorAlternate>();
services.AddTransient<ImageCreatorMapper>();
// Register IImageCreator to be resolved via ImageCreatorMapper which selects concrete implementation at call time
services.AddTransient<IImageCreator>(sp => sp.GetRequiredService<ImageCreatorMapper>());
// Register a ParametriSetup singleton that persists user preferences in LocalApplicationData
var userPrefsPath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),