using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using PLib.Desktop.Imaging;
using PLib.Desktop.Services;
using PLib.Desktop.Settings;
using PLib.Desktop.ViewModels;
using PLib.Infrastructure;
using PLib.Infrastructure.Storage;
using Serilog;
namespace PLib.Desktop;
///
/// Composition root. Everything the application is made of is wired up here and nowhere else.
///
internal static class AppHost
{
public static IHost Create(string[] args)
{
// The paths are needed to locate the user settings file, which is itself a
// configuration source — so they are built before the container exists and then
// handed to it as an instance.
var paths = new AppPaths();
// A desktop app is launched from arbitrary working directories, so the content root
// has to be the folder the executable lives in rather than Environment.CurrentDirectory.
var builder = Host.CreateApplicationBuilder(new HostApplicationBuilderSettings
{
Args = args,
ContentRootPath = AppContext.BaseDirectory,
});
builder.Configuration.AddJsonFile(
Path.Combine(paths.DataDirectory, "settings.json"),
optional: true,
reloadOnChange: true);
ConfigureLogging(builder, paths);
builder.Services.AddSingleton(paths);
builder.Services.AddOptions()
.Bind(builder.Configuration.GetSection(AppearanceOptions.SectionName));
builder.Services.AddOptions()
.Bind(builder.Configuration.GetSection(PlaybackOptions.SectionName))
.ValidateDataAnnotations();
builder.Services.AddPLibInfrastructure(builder.Configuration);
builder.Services.AddSingleton();
builder.Services.AddSingleton(sp => sp.GetRequiredService());
builder.Services.AddSingleton();
builder.Services.AddSingleton();
builder.Services.AddSingleton();
builder.Services.AddSingleton();
builder.Services.AddSingleton();
builder.Services.AddSingleton();
// Scoped, not singleton: the settings dialog edits a working copy, so each opening
// gets its own model and a cancelled edit never leaks into the next one.
builder.Services.AddScoped();
return builder.Build();
}
private static void ConfigureLogging(HostApplicationBuilder builder, IAppPaths paths)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.WriteTo.Console()
.WriteTo.File(
Path.Combine(paths.DataDirectory, "logs", "plib-.log"),
rollingInterval: RollingInterval.Day,
retainedFileCountLimit: 7)
.CreateLogger();
builder.Logging.ClearProviders();
builder.Logging.AddSerilog(Log.Logger, dispose: true);
}
}