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); } }