plugin ui
All checks were successful
Build / Compute Version (push) Successful in 17s
Build / Build Nuget package (CringeBootstrap.Abstractions) (push) Successful in 3m5s
Build / Build Nuget package (NuGet) (push) Successful in 2m34s
Build / Build Nuget package (CringePlugins) (push) Successful in 2m56s
Build / Build Nuget package (SharedCringe) (push) Successful in 1m52s
Build / Build Launcher (push) Successful in 3m52s
All checks were successful
Build / Compute Version (push) Successful in 17s
Build / Build Nuget package (CringeBootstrap.Abstractions) (push) Successful in 3m5s
Build / Build Nuget package (NuGet) (push) Successful in 2m34s
Build / Build Nuget package (CringePlugins) (push) Successful in 2m56s
Build / Build Nuget package (SharedCringe) (push) Successful in 1m52s
Build / Build Launcher (push) Successful in 3m52s
This commit is contained in:
@@ -18,7 +18,7 @@
|
|||||||
<Publicize Include="Sandbox.Game:Sandbox.Engine.Platform.Game.set_DrawThread" />
|
<Publicize Include="Sandbox.Game:Sandbox.Engine.Platform.Game.set_DrawThread" />
|
||||||
<Publicize Include="Sandbox.Game:Sandbox.MySandboxGame.form" />
|
<Publicize Include="Sandbox.Game:Sandbox.MySandboxGame.form" />
|
||||||
<Publicize Include="Sandbox.Game:Sandbox.MySandboxGame.RenderThread_SizeChanged" />
|
<Publicize Include="Sandbox.Game:Sandbox.MySandboxGame.RenderThread_SizeChanged" />
|
||||||
<Publicize Include="VRage.Render11;VRage.Platform.Windows;VRage.Scripting" />
|
<Publicize Include="VRage.Render;VRage.Render11;VRage.Platform.Windows;VRage.Scripting" IncludeCompilerGeneratedMembers="false" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@@ -14,20 +14,18 @@ namespace CringeLauncher;
|
|||||||
|
|
||||||
internal class ImGuiHandler : IDisposable
|
internal class ImGuiHandler : IDisposable
|
||||||
{
|
{
|
||||||
private readonly DeviceContext _deviceContext;
|
private DeviceContext? _deviceContext;
|
||||||
private static nint _wndproc;
|
private static nint _wndproc;
|
||||||
|
|
||||||
public static ImGuiHandler? Instance;
|
public static ImGuiHandler? Instance;
|
||||||
|
|
||||||
public static RenderTargetView? Rtv;
|
public static RenderTargetView? Rtv;
|
||||||
private readonly IRootRenderComponent _renderHandler;
|
private readonly IRootRenderComponent _renderHandler = new RenderHandler();
|
||||||
|
|
||||||
public ImGuiHandler(nint windowHandle, Device1 device, DeviceContext deviceContext)
|
public void Init(nint windowHandle, Device1 device, DeviceContext deviceContext)
|
||||||
{
|
{
|
||||||
_deviceContext = deviceContext;
|
_deviceContext = deviceContext;
|
||||||
|
|
||||||
_renderHandler = new RenderHandler();
|
|
||||||
|
|
||||||
CreateContext();
|
CreateContext();
|
||||||
|
|
||||||
var io = GetIO();
|
var io = GetIO();
|
||||||
@@ -64,7 +62,7 @@ internal class ImGuiHandler : IDisposable
|
|||||||
|
|
||||||
Render();
|
Render();
|
||||||
|
|
||||||
_deviceContext.ClearState();
|
_deviceContext!.ClearState();
|
||||||
_deviceContext.OutputMerger.SetRenderTargets(Rtv);
|
_deviceContext.OutputMerger.SetRenderTargets(Rtv);
|
||||||
|
|
||||||
ImGui_ImplDX11_RenderDrawData(GetDrawData());
|
ImGui_ImplDX11_RenderDrawData(GetDrawData());
|
||||||
@@ -87,7 +85,7 @@ internal class ImGuiHandler : IDisposable
|
|||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_deviceContext.Dispose();
|
_deviceContext?.Dispose();
|
||||||
_renderHandler.Dispose();
|
_renderHandler.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -101,8 +101,8 @@ public class Launcher : ICorePlugin
|
|||||||
_renderComponent = new();
|
_renderComponent = new();
|
||||||
_renderComponent.Start(new(), InitEarlyWindow, MyVideoSettingsManager.Initialize(), MyPerGameSettings.MaxFrameRate);
|
_renderComponent.Start(new(), InitEarlyWindow, MyVideoSettingsManager.Initialize(), MyPerGameSettings.MaxFrameRate);
|
||||||
_renderComponent.RenderThread.BeforeDraw += MyFpsManager.Update;
|
_renderComponent.RenderThread.BeforeDraw += MyFpsManager.Update;
|
||||||
_renderComponent.RenderThread.SizeChanged += RenderThreadOnSizeChanged;
|
|
||||||
|
|
||||||
|
// this technically should wait for render thread init, but who cares
|
||||||
splash.ExecuteLoadingStages();
|
splash.ExecuteLoadingStages();
|
||||||
|
|
||||||
InitUgc();
|
InitUgc();
|
||||||
@@ -118,21 +118,20 @@ public class Launcher : ICorePlugin
|
|||||||
_renderComponent.RenderThread.UpdateSize();
|
_renderComponent.RenderThread.UpdateSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RenderThreadOnSizeChanged(int width, int height, MyViewport viewport)
|
|
||||||
{
|
|
||||||
if (_renderComponent is not null)
|
|
||||||
_renderComponent.RenderThread.SizeChanged -= RenderThreadOnSizeChanged;
|
|
||||||
|
|
||||||
MyVRage.Platform.Windows.Window.ShowAndFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Run() => _game?.Run();
|
public void Run() => _game?.Run();
|
||||||
|
|
||||||
private static IVRageWindow InitEarlyWindow()
|
private IVRageWindow InitEarlyWindow()
|
||||||
{
|
{
|
||||||
|
ImGuiHandler.Instance = new();
|
||||||
|
|
||||||
MyVRage.Platform.Windows.CreateWindow("Cringe Launcher", MyPerGameSettings.GameIcon, null);
|
MyVRage.Platform.Windows.CreateWindow("Cringe Launcher", MyPerGameSettings.GameIcon, null);
|
||||||
|
|
||||||
MyVRage.Platform.Windows.Window.OnExit += MySandboxGame.ExitThreadSafe;
|
MyVRage.Platform.Windows.Window.OnExit += MySandboxGame.ExitThreadSafe;
|
||||||
|
|
||||||
|
_renderComponent!.RenderThread.UpdateSize();
|
||||||
|
MyRenderProxy.RenderThread = _renderComponent.RenderThread;
|
||||||
|
|
||||||
|
MyVRage.Platform.Windows.Window.ShowAndFocus();
|
||||||
|
|
||||||
return MyVRage.Platform.Windows.Window;
|
return MyVRage.Platform.Windows.Window;
|
||||||
}
|
}
|
||||||
@@ -157,7 +156,8 @@ public class Launcher : ICorePlugin
|
|||||||
|
|
||||||
private static void InitThreadPool()
|
private static void InitThreadPool()
|
||||||
{
|
{
|
||||||
ParallelTasks.Parallel.Scheduler = new ThreadPoolScheduler();
|
// ParallelTasks.Parallel.Scheduler = new ThreadPoolScheduler();
|
||||||
|
MySandboxGame.InitMultithreading();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ConfigureSettings()
|
private static void ConfigureSettings()
|
||||||
|
@@ -60,7 +60,7 @@ public static class SwapChainPatch
|
|||||||
[HarmonyPostfix, HarmonyPatch(typeof(MyRender11), nameof(MyRender11.CreateDeviceInternal))]
|
[HarmonyPostfix, HarmonyPatch(typeof(MyRender11), nameof(MyRender11.CreateDeviceInternal))]
|
||||||
private static void CreateDevicePostfix()
|
private static void CreateDevicePostfix()
|
||||||
{
|
{
|
||||||
ImGuiHandler.Instance ??= new ImGuiHandler(WindowHandle, MyRender11.DeviceInstance, MyRender11.RC.DeviceContext);
|
ImGuiHandler.Instance?.Init(WindowHandle, MyRender11.DeviceInstance, MyRender11.RC.DeviceContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HarmonyPrefix, HarmonyPatch(typeof(MyBackbuffer), MethodType.Constructor, typeof(SharpDX.Direct3D11.Resource))]
|
[HarmonyPrefix, HarmonyPatch(typeof(MyBackbuffer), MethodType.Constructor, typeof(SharpDX.Direct3D11.Resource))]
|
||||||
|
@@ -8,6 +8,7 @@ using CringeTask = ParallelTasks.Task;
|
|||||||
|
|
||||||
namespace CringeLauncher.Utils;
|
namespace CringeLauncher.Utils;
|
||||||
|
|
||||||
|
/*
|
||||||
public class ThreadPoolScheduler : IWorkScheduler
|
public class ThreadPoolScheduler : IWorkScheduler
|
||||||
{
|
{
|
||||||
public void Schedule(CringeTask item)
|
public void Schedule(CringeTask item)
|
||||||
@@ -82,4 +83,4 @@ internal class ThreadPoolWorkItemTask(CringeTask task) : IThreadPoolWorkItem
|
|||||||
HkBaseSystem.QuitThread();
|
HkBaseSystem.QuitThread();
|
||||||
Debug.WriteLine($"Hk Shutdown for {Thread.CurrentThread.Name}");
|
Debug.WriteLine($"Hk Shutdown for {Thread.CurrentThread.Name}");
|
||||||
}
|
}
|
||||||
}
|
}*/
|
@@ -8,9 +8,7 @@ namespace CringePlugins.Config;
|
|||||||
public record PackagesConfig(ImmutableArray<PackageSource> Sources, ImmutableArray<PackageReference> Packages)
|
public record PackagesConfig(ImmutableArray<PackageSource> Sources, ImmutableArray<PackageReference> Packages)
|
||||||
{
|
{
|
||||||
public static PackagesConfig Default { get; } = new([
|
public static PackagesConfig Default { get; } = new([
|
||||||
new("SpaceEngineersDedicated.ReferenceAssemblies", "https://ng.zznty.ru/v3/index.json"),
|
new(@"^SpaceEngineersDedicated\.ReferenceAssemblies$|^ImGui\.NET\.DirectX$|^NuGet$|^Cringe.+$|^SharedCringe$|^Plugin.+$", "https://ng.zznty.ru/v3/index.json"),
|
||||||
new("ImGui.NET.DirectX", "https://ng.zznty.ru/v3/index.json"),
|
|
||||||
new("Plugin", "https://ng.zznty.ru/v3/index.json"),
|
|
||||||
new(string.Empty, "https://api.nuget.org/v3/index.json")
|
new(string.Empty, "https://api.nuget.org/v3/index.json")
|
||||||
], []);
|
], []);
|
||||||
}
|
}
|
@@ -2,8 +2,10 @@
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using CringePlugins.Config;
|
using CringePlugins.Config;
|
||||||
|
using CringePlugins.Render;
|
||||||
using CringePlugins.Resolver;
|
using CringePlugins.Resolver;
|
||||||
using CringePlugins.Splash;
|
using CringePlugins.Splash;
|
||||||
|
using CringePlugins.Ui;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NuGet;
|
using NuGet;
|
||||||
using NuGet.Deps;
|
using NuGet.Deps;
|
||||||
@@ -63,6 +65,8 @@ public class PluginsLifetime : ILoadingStage
|
|||||||
progress.Report("Registering plugins");
|
progress.Report("Registering plugins");
|
||||||
|
|
||||||
RegisterLifetime();
|
RegisterLifetime();
|
||||||
|
|
||||||
|
RenderHandler.Current.RegisterComponent(new PluginListComponent(packagesConfig, sourceMapping, configPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RegisterLifetime()
|
private void RegisterLifetime()
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using CringePlugins.Abstractions;
|
using CringePlugins.Abstractions;
|
||||||
|
using ImGuiNET;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
|
||||||
namespace CringePlugins.Render;
|
namespace CringePlugins.Render;
|
||||||
@@ -25,6 +26,10 @@ public sealed class RenderHandler : IRootRenderComponent
|
|||||||
|
|
||||||
void IRenderComponent.OnFrame()
|
void IRenderComponent.OnFrame()
|
||||||
{
|
{
|
||||||
|
#if DEBUG
|
||||||
|
ImGui.ShowDemoWindow();
|
||||||
|
#endif
|
||||||
|
|
||||||
foreach (var (instanceType, renderComponent) in _components)
|
foreach (var (instanceType, renderComponent) in _components)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@@ -53,7 +53,12 @@ public static class BuiltInPackages
|
|||||||
se,
|
se,
|
||||||
imGui,
|
imGui,
|
||||||
harmony,
|
harmony,
|
||||||
FromAssembly<PluginsLifetime>(runtimeFramework, [se.AsDependency(), imGui.AsDependency(), harmony.AsDependency()]),
|
FromAssembly<PluginsLifetime>(runtimeFramework,
|
||||||
|
[se.AsDependency(), imGui.AsDependency(), harmony.AsDependency()]
|
||||||
|
#if DEBUG
|
||||||
|
, version: new(0, 1, 4)
|
||||||
|
#endif
|
||||||
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -21,7 +21,7 @@ public class PackageResolver(NuGetFramework runtimeFramework, ImmutableArray<Pac
|
|||||||
var registrationRoot = await client.GetPackageRegistrationRootAsync(reference.Id);
|
var registrationRoot = await client.GetPackageRegistrationRootAsync(reference.Id);
|
||||||
|
|
||||||
var items = registrationRoot.Items.SelectMany(page =>
|
var items = registrationRoot.Items.SelectMany(page =>
|
||||||
page.Items.Where(b => b.CatalogEntry.PackageTypes is ["CringePlugin"]))
|
page.Items!.Where(b => b.CatalogEntry.PackageTypes is ["CringePlugin"]))
|
||||||
.ToImmutableDictionary(b => b.CatalogEntry.Version);
|
.ToImmutableDictionary(b => b.CatalogEntry.Version);
|
||||||
|
|
||||||
var version = reference.Range.FindBestMatch(items.Values.Select(b => b.CatalogEntry.Version));
|
var version = reference.Range.FindBestMatch(items.Values.Select(b => b.CatalogEntry.Version));
|
||||||
@@ -56,8 +56,11 @@ public class PackageResolver(NuGetFramework runtimeFramework, ImmutableArray<Pac
|
|||||||
foreach (var (package, catalogEntry) in packages)
|
foreach (var (package, catalogEntry) in packages)
|
||||||
{
|
{
|
||||||
var client = await packageSources.GetClientAsync(package.Id);
|
var client = await packageSources.GetClientAsync(package.Id);
|
||||||
|
|
||||||
|
if (!catalogEntry.DependencyGroups.HasValue)
|
||||||
|
continue;
|
||||||
|
|
||||||
var nearestGroup = NuGetFrameworkUtility.GetNearest(catalogEntry.DependencyGroups, runtimeFramework,
|
var nearestGroup = NuGetFrameworkUtility.GetNearest(catalogEntry.DependencyGroups.Value, runtimeFramework,
|
||||||
g => g.TargetFramework);
|
g => g.TargetFramework);
|
||||||
|
|
||||||
if (nearestGroup is null)
|
if (nearestGroup is null)
|
||||||
|
310
CringePlugins/Ui/PluginListComponent.cs
Normal file
310
CringePlugins/Ui/PluginListComponent.cs
Normal file
@@ -0,0 +1,310 @@
|
|||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Text.Json;
|
||||||
|
using CringePlugins.Abstractions;
|
||||||
|
using CringePlugins.Config;
|
||||||
|
using CringePlugins.Resolver;
|
||||||
|
using ImGuiNET;
|
||||||
|
using NLog;
|
||||||
|
using NuGet;
|
||||||
|
using NuGet.Models;
|
||||||
|
using NuGet.Versioning;
|
||||||
|
using Sandbox.Graphics.GUI;
|
||||||
|
using SpaceEngineers.Game.GUI;
|
||||||
|
using static ImGuiNET.ImGui;
|
||||||
|
|
||||||
|
namespace CringePlugins.Ui;
|
||||||
|
|
||||||
|
public class PluginListComponent : IRenderComponent
|
||||||
|
{
|
||||||
|
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
private ImmutableDictionary<string, VersionRange> _packages;
|
||||||
|
private ImmutableDictionary<NuGetClient, SearchResult>? _searchResults;
|
||||||
|
private string _searchQuery = "";
|
||||||
|
private Task? _searchTask;
|
||||||
|
|
||||||
|
private bool _changed;
|
||||||
|
private bool _open = true;
|
||||||
|
private readonly PackagesConfig _packagesConfig;
|
||||||
|
private readonly PackageSourceMapping _sources;
|
||||||
|
private readonly string _configPath;
|
||||||
|
private (SearchResultEntry entry, NuGetClient client)? _selected;
|
||||||
|
|
||||||
|
public PluginListComponent(PackagesConfig packagesConfig, PackageSourceMapping sources, string configPath)
|
||||||
|
{
|
||||||
|
_packagesConfig = packagesConfig;
|
||||||
|
_sources = sources;
|
||||||
|
_configPath = configPath;
|
||||||
|
_packages = packagesConfig.Packages.ToImmutableDictionary(b => b.Id, b => b.Range, StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
MyGuiSandbox.GuiControlCreated += GuiControlCreated;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GuiControlCreated(object obj)
|
||||||
|
{
|
||||||
|
if (obj is MyGuiScreenMainMenu)
|
||||||
|
_open = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnFrame()
|
||||||
|
{
|
||||||
|
if (!_open) return;
|
||||||
|
|
||||||
|
SetNextWindowSize(new(700, 500), ImGuiCond.FirstUseEver);
|
||||||
|
|
||||||
|
if (!Begin("Plugin List", ref _open))
|
||||||
|
{
|
||||||
|
End();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_changed)
|
||||||
|
TextDisabled("Changes would be applied on the next restart");
|
||||||
|
|
||||||
|
if (BeginTabBar("Main"))
|
||||||
|
{
|
||||||
|
// TODO support for opening plugin loader plugin config (reflection call to a specific method)
|
||||||
|
if (BeginTabItem("Installed Plugins"))
|
||||||
|
{
|
||||||
|
if (BeginTable("InstalledTable", 2, ImGuiTableFlags.ScrollY))
|
||||||
|
{
|
||||||
|
TableSetupColumn("Id");
|
||||||
|
TableSetupColumn("Version");
|
||||||
|
TableHeadersRow();
|
||||||
|
|
||||||
|
foreach (var (id, versionRange) in _packages)
|
||||||
|
{
|
||||||
|
TableNextRow();
|
||||||
|
|
||||||
|
TableNextColumn();
|
||||||
|
Text(id);
|
||||||
|
TableNextColumn();
|
||||||
|
Text(versionRange.MinVersion?.ToString() ?? versionRange.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
EndTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
EndTabItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BeginTabItem("Available Plugins"))
|
||||||
|
{
|
||||||
|
AvailablePluginsTab();
|
||||||
|
|
||||||
|
EndTabItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
EndTabBar();
|
||||||
|
}
|
||||||
|
|
||||||
|
End();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO sources editor
|
||||||
|
// TODO combobox with active sources (to limit search results to specific list of sources)
|
||||||
|
private unsafe void AvailablePluginsTab()
|
||||||
|
{
|
||||||
|
if (_searchResults is null && _searchTask is null)
|
||||||
|
{
|
||||||
|
_searchTask = RefreshAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (_searchTask)
|
||||||
|
{
|
||||||
|
case { IsCompleted: false }:
|
||||||
|
TextDisabled("Loading...");
|
||||||
|
return;
|
||||||
|
case { IsCompletedSuccessfully: false }:
|
||||||
|
{
|
||||||
|
TextDisabled("Failed to load plugins list");
|
||||||
|
if (_searchTask.Exception is null) return;
|
||||||
|
|
||||||
|
foreach (var exception in _searchTask.Exception.InnerExceptions)
|
||||||
|
{
|
||||||
|
TextWrapped($"{exception.GetType()}: {exception.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var searchResults = _searchResults ?? ImmutableDictionary<NuGetClient, SearchResult>.Empty;
|
||||||
|
|
||||||
|
InputText("", ref _searchQuery, 256);
|
||||||
|
|
||||||
|
SameLine();
|
||||||
|
|
||||||
|
if (Button("Search"))
|
||||||
|
{
|
||||||
|
_searchTask = RefreshAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (searchResults.IsEmpty || searchResults.Values.All(b => b.Entries.IsEmpty))
|
||||||
|
{
|
||||||
|
TextDisabled("Nothing found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BeginChild("List", new(400, 0), ImGuiChildFlags.Border | ImGuiChildFlags.ResizeX);
|
||||||
|
{
|
||||||
|
if (BeginTable("AvailableTable", 3, ImGuiTableFlags.ScrollY | ImGuiTableFlags.Resizable | ImGuiTableFlags.SizingStretchProp))
|
||||||
|
{
|
||||||
|
TableSetupColumn("Id", ImGuiTableColumnFlags.None, .5f);
|
||||||
|
TableSetupColumn("Version", ImGuiTableColumnFlags.None, .3f);
|
||||||
|
TableSetupColumn("Installed", ImGuiTableColumnFlags.None, .2f);
|
||||||
|
TableHeadersRow();
|
||||||
|
|
||||||
|
foreach (var (client, result) in searchResults)
|
||||||
|
{
|
||||||
|
foreach (var package in result.Entries.Take(100))
|
||||||
|
{
|
||||||
|
TableNextRow();
|
||||||
|
|
||||||
|
TableNextColumn();
|
||||||
|
|
||||||
|
var selected = _selected?.entry.Id.Equals(package.Id, StringComparison.OrdinalIgnoreCase) == true;
|
||||||
|
|
||||||
|
if (Selectable(package.Title ?? package.Id, ref selected, ImGuiSelectableFlags.SpanAllColumns))
|
||||||
|
{
|
||||||
|
_selected = selected ? (package, client) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(package.Summary) && IsItemHovered(ImGuiHoveredFlags.ForTooltip))
|
||||||
|
{
|
||||||
|
SetTooltip(package.Summary);
|
||||||
|
}
|
||||||
|
|
||||||
|
TableNextColumn();
|
||||||
|
Text(package.Version.ToString());
|
||||||
|
|
||||||
|
TableNextColumn();
|
||||||
|
|
||||||
|
var installed = _packages.ContainsKey(package.Id);
|
||||||
|
BeginDisabled();
|
||||||
|
if (Checkbox("", ref installed))
|
||||||
|
{
|
||||||
|
_packages = installed
|
||||||
|
? _packages.Add(package.Id, new(package.Version))
|
||||||
|
: _packages.Remove(package.Id);
|
||||||
|
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
EndDisabled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EndTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
EndChild();
|
||||||
|
}
|
||||||
|
|
||||||
|
SameLine();
|
||||||
|
|
||||||
|
BeginGroup();
|
||||||
|
|
||||||
|
BeginChild("Package View", new(0, -GetFrameHeightWithSpacing())); // Leave room for 1 line below us
|
||||||
|
|
||||||
|
if (_selected is not null)
|
||||||
|
{
|
||||||
|
var selected = _selected.Value.entry;
|
||||||
|
|
||||||
|
Text(selected.Title ?? selected.Id);
|
||||||
|
SameLine();
|
||||||
|
TextColored(*GetStyleColorVec4(ImGuiCol.TextLink), selected.Version.ToString());
|
||||||
|
Separator();
|
||||||
|
if (BeginTabBar("##PackageViewTabs"))
|
||||||
|
{
|
||||||
|
if (BeginTabItem("Description"))
|
||||||
|
{
|
||||||
|
TextWrapped(selected.Description ?? "Nothing.");
|
||||||
|
EndTabItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BeginTabItem("Details"))
|
||||||
|
{
|
||||||
|
Text("Pulled from");
|
||||||
|
SameLine();
|
||||||
|
var url = _selected.Value.client.ToString();
|
||||||
|
TextLinkOpenURL(url, url);
|
||||||
|
|
||||||
|
if (selected.Authors is not null)
|
||||||
|
{
|
||||||
|
Text("Authors:");
|
||||||
|
SameLine();
|
||||||
|
TextWrapped(selected.Authors.Author);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selected.TotalDownloads.HasValue)
|
||||||
|
{
|
||||||
|
Text("Total downloads:");
|
||||||
|
SameLine();
|
||||||
|
TextColored(*GetStyleColorVec4(ImGuiCol.TextLink), selected.TotalDownloads.ToString());
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrEmpty(selected.ProjectUrl))
|
||||||
|
TextLinkOpenURL("Project URL", selected.ProjectUrl);
|
||||||
|
|
||||||
|
EndTabItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
EndTabBar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EndChild();
|
||||||
|
|
||||||
|
if (_selected is not null)
|
||||||
|
{
|
||||||
|
var selected = _selected.Value.entry;
|
||||||
|
|
||||||
|
var installed = _packages.ContainsKey(selected.Id);
|
||||||
|
if (Button(installed ? "Uninstall" : "Install"))
|
||||||
|
{
|
||||||
|
_packages = installed
|
||||||
|
? _packages.Remove(selected.Id)
|
||||||
|
: _packages.Add(selected.Id, new(selected.Version));
|
||||||
|
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RefreshAsync()
|
||||||
|
{
|
||||||
|
_searchResults = null;
|
||||||
|
|
||||||
|
var builder = ImmutableDictionary.CreateBuilder<NuGetClient, SearchResult>();
|
||||||
|
|
||||||
|
await foreach (var source in _sources)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await source.SearchPackagesAsync(_searchQuery, take: 1000, packageType: "CringePlugin");
|
||||||
|
|
||||||
|
builder.Add(source, result);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error(e, "Failed to get packages from source {Source}", source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_searchResults = builder.ToImmutable();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Save()
|
||||||
|
{
|
||||||
|
_changed = true;
|
||||||
|
|
||||||
|
using var stream = File.Create(_configPath);
|
||||||
|
|
||||||
|
JsonSerializer.Serialize(stream, _packagesConfig with
|
||||||
|
{
|
||||||
|
Packages = [.._packages.Select(b => new PackageReference(b.Key, b.Value))]
|
||||||
|
}, NuGetClient.SerializerOptions);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<configuration>
|
<configuration>
|
||||||
<packageSources>
|
<packageSources>
|
||||||
<add key="zznty" value="https://nuget.storage.yandexcloud.net/index.json" protocolVersion="3" />
|
<add key="zznty" value="https://ng.zznty.ru/v3/index.json" protocolVersion="3" />
|
||||||
</packageSources>
|
</packageSources>
|
||||||
</configuration>
|
</configuration>
|
54
NuGet/Converters/PackageAuthorsJsonConverter.cs
Normal file
54
NuGet/Converters/PackageAuthorsJsonConverter.cs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using NuGet.Models;
|
||||||
|
|
||||||
|
namespace NuGet.Converters;
|
||||||
|
|
||||||
|
public class PackageAuthorsJsonConverter : JsonConverter<PackageAuthors>
|
||||||
|
{
|
||||||
|
public override PackageAuthors? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
switch (reader.TokenType)
|
||||||
|
{
|
||||||
|
case JsonTokenType.String:
|
||||||
|
{
|
||||||
|
var author = reader.GetString()!;
|
||||||
|
return new PackageAuthors(author, [author]);
|
||||||
|
}
|
||||||
|
case JsonTokenType.StartArray:
|
||||||
|
{
|
||||||
|
var builder = ImmutableArray.CreateBuilder<string>();
|
||||||
|
|
||||||
|
while (reader.Read() && reader.TokenType != JsonTokenType.EndArray)
|
||||||
|
{
|
||||||
|
builder.Add(reader.GetString()!);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PackageAuthors(string.Join(", ", builder), builder.ToImmutable());
|
||||||
|
}
|
||||||
|
case JsonTokenType.Null:
|
||||||
|
return null;
|
||||||
|
default:
|
||||||
|
throw new JsonException("String or array of strings expected");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(Utf8JsonWriter writer, PackageAuthors value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
if (value.Authors.Length == 1)
|
||||||
|
{
|
||||||
|
writer.WriteStringValue(value.Author);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteStartArray();
|
||||||
|
|
||||||
|
foreach (var author in value.Authors)
|
||||||
|
{
|
||||||
|
writer.WriteStringValue(author);
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteEndArray();
|
||||||
|
}
|
||||||
|
}
|
48
NuGet/Converters/StringOrStringArrayConverter.cs
Normal file
48
NuGet/Converters/StringOrStringArrayConverter.cs
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace NuGet.Converters;
|
||||||
|
|
||||||
|
public class StringOrStringArrayConverter : JsonConverter<ImmutableArray<string>>
|
||||||
|
{
|
||||||
|
public override ImmutableArray<string> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
switch (reader.TokenType)
|
||||||
|
{
|
||||||
|
case JsonTokenType.String:
|
||||||
|
return [reader.GetString()!];
|
||||||
|
case JsonTokenType.StartArray:
|
||||||
|
{
|
||||||
|
var builder = ImmutableArray.CreateBuilder<string>();
|
||||||
|
|
||||||
|
while (reader.Read() && reader.TokenType != JsonTokenType.EndArray)
|
||||||
|
{
|
||||||
|
builder.Add(reader.GetString()!);
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.ToImmutable();
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new JsonException("String or array of strings expected");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(Utf8JsonWriter writer, ImmutableArray<string> value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
if (value.Length == 1)
|
||||||
|
{
|
||||||
|
writer.WriteStringValue(value[0]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteStartArray();
|
||||||
|
|
||||||
|
foreach (var author in value)
|
||||||
|
{
|
||||||
|
writer.WriteStringValue(author);
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteEndArray();
|
||||||
|
}
|
||||||
|
}
|
@@ -88,10 +88,10 @@ public class DependencyManifestBuilder(DirectoryInfo cacheDirectory, PackageSour
|
|||||||
private async Task MapCatalogEntryAsync(CatalogEntry catalogEntry, NuGetFramework targetFramework,
|
private async Task MapCatalogEntryAsync(CatalogEntry catalogEntry, NuGetFramework targetFramework,
|
||||||
ImmutableDictionary<ManifestPackageKey, DependencyTarget>.Builder targets)
|
ImmutableDictionary<ManifestPackageKey, DependencyTarget>.Builder targets)
|
||||||
{
|
{
|
||||||
if (targets.ContainsKey(new(catalogEntry.Id, catalogEntry.Version)))
|
if (targets.ContainsKey(new(catalogEntry.Id, catalogEntry.Version)) || !catalogEntry.DependencyGroups.HasValue)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var nearest = NuGetFrameworkUtility.GetNearest(catalogEntry.DependencyGroups, targetFramework,
|
var nearest = NuGetFrameworkUtility.GetNearest(catalogEntry.DependencyGroups.Value, targetFramework,
|
||||||
group => group.TargetFramework);
|
group => group.TargetFramework);
|
||||||
|
|
||||||
if (nearest is null)
|
if (nearest is null)
|
||||||
@@ -103,9 +103,18 @@ public class DependencyManifestBuilder(DirectoryInfo cacheDirectory, PackageSour
|
|||||||
foreach (var dependency in nearest.Dependencies ?? [])
|
foreach (var dependency in nearest.Dependencies ?? [])
|
||||||
{
|
{
|
||||||
var client = await packageSources.GetClientAsync(dependency.Id);
|
var client = await packageSources.GetClientAsync(dependency.Id);
|
||||||
var (url, entry) = await client.GetPackageRegistrationAsync(dependency.Id, versionResolver(dependency)!);
|
var registrationRoot = await client.GetPackageRegistrationRootAsync(dependency.Id);
|
||||||
|
|
||||||
entry ??= await client.GetPackageCatalogEntryAsync(url);
|
var version = versionResolver(dependency)!;
|
||||||
|
var entry = registrationRoot.Items.SelectMany(b => b.Items ?? []).FirstOrDefault(b => b.CatalogEntry.Version == version)?.CatalogEntry;
|
||||||
|
|
||||||
|
if (entry is null)
|
||||||
|
{
|
||||||
|
var (url, sleetEntry) = await client.GetPackageRegistrationAsync(dependency.Id, versionResolver(dependency)!);
|
||||||
|
|
||||||
|
entry = sleetEntry;
|
||||||
|
entry ??= await client.GetPackageCatalogEntryAsync(url);
|
||||||
|
}
|
||||||
|
|
||||||
await MapCatalogEntryAsync(entry, targetFramework, targets);
|
await MapCatalogEntryAsync(entry, targetFramework, targets);
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,7 @@ using NuGet.Versioning;
|
|||||||
|
|
||||||
namespace NuGet.Models;
|
namespace NuGet.Models;
|
||||||
|
|
||||||
public record CatalogEntry(string Id, NuGetVersion Version, ImmutableArray<DependencyGroup> DependencyGroups, ImmutableArray<string>? PackageTypes,
|
public record CatalogEntry(string Id, NuGetVersion Version, ImmutableArray<DependencyGroup>? DependencyGroups, ImmutableArray<string>? PackageTypes,
|
||||||
ImmutableArray<CatalogPackageEntry>? PackageEntries);
|
ImmutableArray<CatalogPackageEntry>? PackageEntries);
|
||||||
|
|
||||||
public record CatalogPackageEntry(string Name, string FullName, long CompressedLength, long Length);
|
public record CatalogPackageEntry(string Name, string FullName, long CompressedLength, long Length);
|
8
NuGet/Models/PackageAuthors.cs
Normal file
8
NuGet/Models/PackageAuthors.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using NuGet.Converters;
|
||||||
|
|
||||||
|
namespace NuGet.Models;
|
||||||
|
|
||||||
|
[JsonConverter(typeof(PackageAuthorsJsonConverter))]
|
||||||
|
public record PackageAuthors(string Author, ImmutableArray<string> Authors);
|
3
NuGet/Models/PackageType.cs
Normal file
3
NuGet/Models/PackageType.cs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
namespace NuGet.Models;
|
||||||
|
|
||||||
|
public record PackageType(string Name);
|
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
namespace NuGet.Models;
|
namespace NuGet.Models;
|
||||||
|
|
||||||
public record RegistrationPage(int Count, NuGetVersion Lower, NuGetVersion Upper, RegistrationEntry[] Items);
|
public record RegistrationPage(int Count, NuGetVersion Lower, NuGetVersion Upper, RegistrationEntry[]? Items);
|
33
NuGet/Models/SearchResult.cs
Normal file
33
NuGet/Models/SearchResult.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using NuGet.Converters;
|
||||||
|
using NuGet.Versioning;
|
||||||
|
|
||||||
|
namespace NuGet.Models;
|
||||||
|
|
||||||
|
public record SearchResult(int TotalHits, [property: JsonPropertyName("data")] ImmutableArray<SearchResultEntry> Entries);
|
||||||
|
|
||||||
|
public record SearchResultEntry(
|
||||||
|
string Id,
|
||||||
|
NuGetVersion Version,
|
||||||
|
string? Description,
|
||||||
|
ImmutableArray<SearchResultPackageVersion> Versions,
|
||||||
|
PackageAuthors? Authors,
|
||||||
|
string? IconUrl,
|
||||||
|
string? LicenseUrl,
|
||||||
|
[property: JsonConverter(typeof(StringOrStringArrayConverter))]
|
||||||
|
ImmutableArray<string>? Owners,
|
||||||
|
string? ProjectUrl,
|
||||||
|
Uri Registration,
|
||||||
|
string? Summary,
|
||||||
|
[property: JsonConverter(typeof(StringOrStringArrayConverter))]
|
||||||
|
ImmutableArray<string>? Tags,
|
||||||
|
string? Title,
|
||||||
|
int? TotalDownloads,
|
||||||
|
ImmutableArray<PackageType> PackageTypes,
|
||||||
|
bool Verified = false);
|
||||||
|
|
||||||
|
public record SearchResultPackageVersion(
|
||||||
|
NuGetVersion Version,
|
||||||
|
int Downloads,
|
||||||
|
[property: JsonPropertyName("@id")] Uri Registration);
|
@@ -1,6 +1,7 @@
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http.Json;
|
using System.Net.Http.Json;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using System.Web;
|
||||||
using NuGet.Converters;
|
using NuGet.Converters;
|
||||||
using NuGet.Models;
|
using NuGet.Models;
|
||||||
using NuGet.Versioning;
|
using NuGet.Versioning;
|
||||||
@@ -9,9 +10,11 @@ namespace NuGet;
|
|||||||
|
|
||||||
public class NuGetClient
|
public class NuGetClient
|
||||||
{
|
{
|
||||||
|
private readonly Uri _index;
|
||||||
private readonly HttpClient _client;
|
private readonly HttpClient _client;
|
||||||
private readonly Uri _packageBaseAddress;
|
private readonly Uri _packageBaseAddress;
|
||||||
private readonly Uri _registration;
|
private readonly Uri _registration;
|
||||||
|
private readonly Uri _search;
|
||||||
|
|
||||||
public static JsonSerializerOptions SerializerOptions { get; } = new(JsonSerializerDefaults.Web)
|
public static JsonSerializerOptions SerializerOptions { get; } = new(JsonSerializerDefaults.Web)
|
||||||
{
|
{
|
||||||
@@ -24,11 +27,13 @@ public class NuGetClient
|
|||||||
WriteIndented = true
|
WriteIndented = true
|
||||||
};
|
};
|
||||||
|
|
||||||
private NuGetClient(HttpClient client, Uri packageBaseAddress, Uri registration)
|
private NuGetClient(Uri index, HttpClient client, Uri packageBaseAddress, Uri registration, Uri search)
|
||||||
{
|
{
|
||||||
|
_index = index;
|
||||||
_client = client;
|
_client = client;
|
||||||
_packageBaseAddress = packageBaseAddress;
|
_packageBaseAddress = packageBaseAddress;
|
||||||
_registration = registration;
|
_registration = registration;
|
||||||
|
_search = search;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<Stream> GetPackageContentStreamAsync(string id, NuGetVersion version)
|
public Task<Stream> GetPackageContentStreamAsync(string id, NuGetVersion version)
|
||||||
@@ -61,6 +66,37 @@ public class NuGetClient
|
|||||||
return _client.GetFromJsonAsync<CatalogEntry>(url, SerializerOptions)!;
|
return _client.GetFromJsonAsync<CatalogEntry>(url, SerializerOptions)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task<SearchResult> SearchPackagesAsync(string? query = null, int? skip = null, int? take = null,
|
||||||
|
bool? includePrerelease = null, NuGetVersion? minVersion = null, string? packageType = null)
|
||||||
|
{
|
||||||
|
var queryParameters = HttpUtility.ParseQueryString(string.Empty);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(query))
|
||||||
|
queryParameters.Add("q", query);
|
||||||
|
|
||||||
|
if (skip.HasValue)
|
||||||
|
queryParameters.Add("skip", skip.Value.ToString());
|
||||||
|
|
||||||
|
if (take.HasValue)
|
||||||
|
queryParameters.Add("take", take.Value.ToString());
|
||||||
|
|
||||||
|
if (includePrerelease.HasValue)
|
||||||
|
queryParameters.Add("prerelease", includePrerelease.Value.ToString());
|
||||||
|
|
||||||
|
if (minVersion is not null)
|
||||||
|
queryParameters.Add("semVerLevel", minVersion.ToString());
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(packageType))
|
||||||
|
queryParameters.Add("packageType", packageType);
|
||||||
|
|
||||||
|
var builder = new UriBuilder(_search)
|
||||||
|
{
|
||||||
|
Query = queryParameters.ToString()
|
||||||
|
};
|
||||||
|
|
||||||
|
return _client.GetFromJsonAsync<SearchResult>(builder.Uri, SerializerOptions)!;
|
||||||
|
}
|
||||||
|
|
||||||
public static async Task<NuGetClient> CreateFromIndexUrlAsync(string indexUrl)
|
public static async Task<NuGetClient> CreateFromIndexUrlAsync(string indexUrl)
|
||||||
{
|
{
|
||||||
var client = new HttpClient(new HttpClientHandler
|
var client = new HttpClient(new HttpClientHandler
|
||||||
@@ -71,13 +107,20 @@ public class NuGetClient
|
|||||||
var index = await client.GetFromJsonAsync<NuGetIndex>(indexUrl, SerializerOptions);
|
var index = await client.GetFromJsonAsync<NuGetIndex>(indexUrl, SerializerOptions);
|
||||||
|
|
||||||
var (packageBaseAddress, _, _) = index!.Resources.First(b => b.Type.Id == "PackageBaseAddress");
|
var (packageBaseAddress, _, _) = index!.Resources.First(b => b.Type.Id == "PackageBaseAddress");
|
||||||
var (registration, _, _) = index!.Resources.First(b => b.Type.Id == "RegistrationsBaseUrl");
|
var (registration, _, _) = index.Resources.First(b => b.Type.Id == "RegistrationsBaseUrl");
|
||||||
|
var (search, _, _) = index.Resources.First(b => b.Type.Id == "SearchQueryService");
|
||||||
|
|
||||||
if (!packageBaseAddress.EndsWith('/'))
|
if (!packageBaseAddress.EndsWith('/'))
|
||||||
packageBaseAddress += '/';
|
packageBaseAddress += '/';
|
||||||
if (!registration.EndsWith('/'))
|
if (!registration.EndsWith('/'))
|
||||||
registration += '/';
|
registration += '/';
|
||||||
|
|
||||||
return new NuGetClient(client, new Uri(packageBaseAddress), new Uri(registration));
|
return new NuGetClient(new Uri(indexUrl), client, new Uri(packageBaseAddress), new Uri(registration), new Uri(search));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override string ToString() => _index.ToString();
|
||||||
|
|
||||||
|
public override bool Equals(object? obj) => obj is NuGetClient { _index: { } index } && index == _index;
|
||||||
|
|
||||||
|
public override int GetHashCode() => _index.GetHashCode();
|
||||||
}
|
}
|
@@ -1,4 +1,7 @@
|
|||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace NuGet;
|
namespace NuGet;
|
||||||
|
|
||||||
@@ -11,7 +14,15 @@ public class PackageSourceMapping(ImmutableArray<PackageSource> sources)
|
|||||||
];
|
];
|
||||||
|
|
||||||
public Task<NuGetClient> GetClientAsync(string packageId) =>
|
public Task<NuGetClient> GetClientAsync(string packageId) =>
|
||||||
_clients.FirstOrDefault(b => packageId.StartsWith(b.pattern)).client;
|
_clients.FirstOrDefault(b => Regex.IsMatch(packageId, b.pattern)).client;
|
||||||
|
|
||||||
|
public ConfiguredCancelableAsyncEnumerable<NuGetClient>.Enumerator GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
return _clients.ToAsyncEnumerable()
|
||||||
|
.SelectAwait(b => new ValueTask<NuGetClient>(b.client))
|
||||||
|
.WithCancellation(cancellationToken)
|
||||||
|
.GetAsyncEnumerator();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public record PackageSource(string Pattern, string Url);
|
public record PackageSource([StringSyntax("Regex")] string Pattern, [StringSyntax("Uri")] string Url);
|
Reference in New Issue
Block a user