props/Props/Services/Modules/ModularShopManager.cs
Harrison Deng 8a1e5aca15 Changed Shop interface to be more asynchronous.
Implemented changes in Props.

Implemented said changes in AdafruitShop.

Fixed and improved caching in AdafruitShop.
2021-08-11 23:54:52 -05:00

129 lines
4.9 KiB
C#

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 Task ShopLoadingTask;
private ILoggerFactory loggerFactory;
private ILogger<ModularShopManager> logger;
private Dictionary<string, IShop> shops;
private ModulesOptions options;
private IConfiguration configuration;
public ModularShopManager(IConfiguration configuration, ILogger<ModularShopManager> logger, ILoggerFactory loggerFactory)
{
this.loggerFactory = loggerFactory;
this.logger = logger;
this.configuration = configuration;
options = configuration.GetSection(ModulesOptions.Modules).Get<ModulesOptions>();
Directory.CreateDirectory(options.ModuleDataDir);
shops = new Dictionary<string, IShop>();
ShopLoadingTask = LoadShops();
}
public async ValueTask<IEnumerable<string>> GetAllShopNames()
{
await ShopLoadingTask;
return shops.Keys;
}
public async ValueTask<IShop> GetShop(string name)
{
await ShopLoadingTask;
return shops[name];
}
public async ValueTask<IEnumerable<IShop>> GetAllShops()
{
await ShopLoadingTask;
return shops.Values;
}
private async Task LoadShops()
{
string shopsDir = options.ModulesDir;
string shopRegex = options.ShopRegex;
bool recursiveLoad = options.RecursiveLoad;
Stack<string> directories = new Stack<string>();
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);
await 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);
}
}
}
}
}
public async ValueTask DisposeAsync()
{
logger.LogDebug("Disposing...");
await DisposeAsyncCore();
}
protected virtual async ValueTask DisposeAsyncCore()
{
await ShopLoadingTask;
foreach (string shopName in shops.Keys)
{
await shops[shopName].DisposeAsync();
logger.LogDebug("Completed dispose task for \"{0}\".", shopName);
}
}
}
}