using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Microsoft.VisualStudio.Web.CodeGeneration; using Props.Data; using Props.Models.Search; using Props.Options; using Props.Shop.Framework; namespace Props.Services.Modules { public class ModularShopManager : IShopManager { private ILoggerFactory loggerFactory; private ILogger logger; private Dictionary shops; private ModulesOptions options; private IConfiguration configuration; private bool disposedValue; public ModularShopManager(IConfiguration configuration, ILogger logger, ILoggerFactory loggerFactory) { this.loggerFactory = loggerFactory; this.logger = logger; this.configuration = configuration; options = configuration.GetSection(ModulesOptions.Modules).Get(); Directory.CreateDirectory(options.ModuleDataDir); shops = new Dictionary(); LoadShops(); } public IEnumerable GetAllShopNames() { return shops.Keys; } public IShop GetShop(string name) { return shops[name]; } public IEnumerable GetAllShops() { return shops.Values; } public void LoadShops() { string shopsDir = options.ModulesDir; string shopRegex = options.ShopRegex; bool recursiveLoad = options.RecursiveLoad; Stack directories = new Stack(); directories.Push(shopsDir); string currentDirectory = null; while (directories.TryPop(out currentDirectory)) { if (recursiveLoad) { foreach (string dir in Directory.EnumerateDirectories(currentDirectory)) { directories.Push(dir); } } foreach (string file in Directory.EnumerateFiles(currentDirectory)) { if (Path.GetExtension(file).Equals(".dll") && Regex.IsMatch(file, shopRegex)) { ShopAssemblyLoadContext context = new ShopAssemblyLoadContext(file); Assembly assembly = context.LoadFromAssemblyName(new AssemblyName(Path.GetFileNameWithoutExtension(file))); int success = 0; foreach (Type type in assembly.GetTypes()) { if (typeof(IShop).IsAssignableFrom(type)) { IShop shop = Activator.CreateInstance(type) as IShop; if (shop != null) { DirectoryInfo dataDir = Directory.CreateDirectory(Path.Combine(options.ModuleDataDir, file.Substring(file.IndexOf(Path.DirectorySeparatorChar) + 1))); logger.LogDebug("Checking data directory for \"{0}\" at \"{1}\"", Path.GetFileName(file), dataDir.FullName); shop.Initialize(dataDir.FullName, loggerFactory); success += 1; if (!shops.TryAdd(shop.ShopName, shop)) { logger.LogWarning("Duplicate shop {0} detected. Ignoring the latter.", shop.ShopName); } logger.LogDebug("Loaded \"{0}\".", shop.ShopName); } } } if (success == 0) { logger.LogWarning("There were no shops found within the assembly at path \"{0}\".", file); } } } } } protected virtual void Dispose(bool disposing) { if (!disposedValue) { if (disposing) { foreach (string shopName in shops.Keys) { shops[shopName].SaveData().AsTask().Wait(); shops[shopName].Dispose(); } } disposedValue = true; } } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } } }