diff --git a/sample/BitzArt.Console.TestApp/Menus/0.MainMenu.cs b/sample/BitzArt.Console.TestApp/Menus/0.MainMenu.cs index 56d8bfd..85df7e1 100644 --- a/sample/BitzArt.Console.TestApp/Menus/0.MainMenu.cs +++ b/sample/BitzArt.Console.TestApp/Menus/0.MainMenu.cs @@ -1,13 +1,11 @@ namespace BitzArt.Console; +[AppMenu("Main Menu", IsMain = true)] internal class MainMenu : ConsoleSelectionMenu { - public override string Title => "Main Menu"; - protected override bool IsMainMenu => true; - - public MainMenu() + [MenuSelectionItem("Veggies")] + public async Task SubmenuVeggiesAsync() { - AddSubmenu("1. Fruits"); - AddSubmenu("2. Veggies"); + await RunAsync(); } } diff --git a/sample/BitzArt.Console.TestApp/Menus/1.FruitsMenu.cs b/sample/BitzArt.Console.TestApp/Menus/1.FruitsMenu.cs deleted file mode 100644 index 535d192..0000000 --- a/sample/BitzArt.Console.TestApp/Menus/1.FruitsMenu.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Spectre.Console; - -namespace BitzArt.Console; - -internal class FruitsMenu : ConsoleSelectionMenu -{ - public override string Title => "Fruits"; - - public FruitsMenu() - { - AddSelection("apple", pauseOnComplete: true); - AddSelection("pear", pauseOnComplete: true); - AddSelection("banana", pauseOnComplete: true); - } - - public override void OnSelection(ConsoleSelectionMenuItem selection) - { - AnsiConsole.WriteLine($"You selected '{selection.Name}'"); - } -} diff --git a/sample/BitzArt.Console.TestApp/Menus/2.VeggiesMenu.cs b/sample/BitzArt.Console.TestApp/Menus/1.VeggiesMenu.cs similarity index 53% rename from sample/BitzArt.Console.TestApp/Menus/2.VeggiesMenu.cs rename to sample/BitzArt.Console.TestApp/Menus/1.VeggiesMenu.cs index f8c23a4..cccd922 100644 --- a/sample/BitzArt.Console.TestApp/Menus/2.VeggiesMenu.cs +++ b/sample/BitzArt.Console.TestApp/Menus/1.VeggiesMenu.cs @@ -2,36 +2,43 @@ namespace BitzArt.Console; -internal class VeggiesMenu : ConsoleSelectionMenu +[AppMenu("Veggies")] +internal class VeggiesMenu(VeggyOptions options) : ConsoleSelectionMenu { - public override string Title => "Veggies"; + private bool _focusOnPotato = false; - public VeggyOptions _options; + [MenuSelectionItem("Tomato")] + public static void Tomato() + { + } - private bool _focusOnPotato = false; + [MenuSelectionItem("Cucumber")] + public static void Cucumber() + { + } - public VeggiesMenu(VeggyOptions options) + [MenuSelectionItem("Potato")] + public void Potato() { - AddSelection("tomato", pauseOnComplete: true); - AddSelection("cucumber", pauseOnComplete: true); - AddSelection("is potato a vegetable?", () => _focusOnPotato = true, pauseOnComplete: true); - _options = options; + _focusOnPotato = true; } public override void OnBeforeSelectionInvoke(ConsoleSelectionMenuItem selection) { _focusOnPotato = false; - AnsiConsole.WriteLine($"You selected '{selection.Name}'"); } public override void OnSelection(ConsoleSelectionMenuItem selection) { + AnsiConsole.WriteLine($"You selected '{selection.Name}'"); + if (_focusOnPotato) { - var possibleNegation = _options.IsPotatoAVegetable ? "" : "not "; + var possibleNegation = options.IsPotatoAVegetable ? "" : "not "; var text = $"Application settings say that potato is {possibleNegation}a vegetable."; AnsiConsole.WriteLine(text); - return; } + + Pause(); } } diff --git a/sample/BitzArt.Console.TestApp/Program.cs b/sample/BitzArt.Console.TestApp/Program.cs index 29caeb6..eaf197d 100644 --- a/sample/BitzArt.Console.TestApp/Program.cs +++ b/sample/BitzArt.Console.TestApp/Program.cs @@ -6,11 +6,11 @@ static void Main() { var builder = ConsoleApp.CreateBuilder(); - builder.Services.AddConsoleMenusFromAssemblyContaining(); + builder.AddConsoleMenusFromAssemblyContaining(); builder.AddVeggyOptions(); var app = builder.Build(); - app.Run(); + app.Run(); } } diff --git a/src/BitzArt.Console/App/ConsoleApp.cs b/src/BitzArt.Console/App/ConsoleApp.cs index 8b90cd9..fb7ea99 100644 --- a/src/BitzArt.Console/App/ConsoleApp.cs +++ b/src/BitzArt.Console/App/ConsoleApp.cs @@ -12,7 +12,19 @@ public ConsoleApp(IConsoleAppBuilder builder) Services = builder.Services.BuildServiceProvider(); } - public void Run(CancellationToken cancellationToken = default) where TConsoleMenu : IConsoleMenu + public void Run(CancellationToken cancellationToken = default) + { + var navigationManager = Services.GetRequiredService(); + navigationManager.NavigateToMainMenuAsync().Wait(cancellationToken); + } + + public void Run(Type mainMenuType, CancellationToken cancellationToken = default) + { + var navigationManager = Services.GetRequiredService(); + navigationManager.NavigateAsync(mainMenuType).Wait(cancellationToken); + } + + public void Run(CancellationToken cancellationToken = default) where TConsoleMenu : class { var navigationManager = Services.GetRequiredService(); navigationManager.NavigateAsync().Wait(cancellationToken); diff --git a/src/BitzArt.Console/App/ConsoleAppBuilder.cs b/src/BitzArt.Console/App/ConsoleAppBuilder.cs index 5ce80a2..ee91e48 100644 --- a/src/BitzArt.Console/App/ConsoleAppBuilder.cs +++ b/src/BitzArt.Console/App/ConsoleAppBuilder.cs @@ -8,12 +8,17 @@ internal class ConsoleAppBuilder : IConsoleAppBuilder public IServiceCollection Services { get; private set; } public IConfiguration Configuration { get; private set; } + public MenuMap MenuMap { get; private set; } + public ConsoleAppBuilder() { Services = new ServiceCollection(); Services.AddSingleton(); + MenuMap = new MenuMap(); + Services.AddSingleton(MenuMap); + Configuration = new ConfigurationBuilder() .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .Build(); diff --git a/src/BitzArt.Console/App/IConsoleAppBuilder.cs b/src/BitzArt.Console/App/IConsoleAppBuilder.cs deleted file mode 100644 index 49ff4b1..0000000 --- a/src/BitzArt.Console/App/IConsoleAppBuilder.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; - -namespace BitzArt.Console; - -public interface IConsoleAppBuilder -{ - IConfiguration Configuration { get; } - IServiceCollection Services { get; } - - ConsoleApp Build(); -} \ No newline at end of file diff --git a/src/BitzArt.Console/Attributes/AppMenuAttribute.cs b/src/BitzArt.Console/Attributes/AppMenuAttribute.cs new file mode 100644 index 0000000..da4219d --- /dev/null +++ b/src/BitzArt.Console/Attributes/AppMenuAttribute.cs @@ -0,0 +1,34 @@ +using System.Reflection; + +namespace BitzArt.Console; + +[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] +public class AppMenuAttribute : Attribute +{ + private readonly string _title = "Menu"; + + public bool IsMain { get; set; } = false; + + public AppMenuAttribute(string title) + { + if (string.IsNullOrWhiteSpace(title)) + { + throw new ArgumentException("Menu title cannot be empty.", nameof(title)); + } + + _title = title; + } + + internal string GetTitle() => _title; +} + +internal static class AppMenuAttributeExtensions +{ + public static AppMenuAttribute GetAppMenuAttribute(this Type type) + { + var attribute = type.GetCustomAttribute() + ?? throw new InvalidOperationException($"Menu {type.Name} must have {nameof(AppMenuAttribute)}"); + + return attribute; + } +} diff --git a/src/BitzArt.Console/Attributes/MenuSelectionItemAttribute.cs b/src/BitzArt.Console/Attributes/MenuSelectionItemAttribute.cs new file mode 100644 index 0000000..2286a7d --- /dev/null +++ b/src/BitzArt.Console/Attributes/MenuSelectionItemAttribute.cs @@ -0,0 +1,50 @@ +using System.Reflection; + +namespace BitzArt.Console; + +[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] +public class MenuSelectionItemAttribute : Attribute +{ + private readonly string _name; + public bool PauseOnComplete { get; set; } = false; + + public MenuSelectionItemAttribute(string name) + { + if (string.IsNullOrWhiteSpace(name)) + { + throw new ArgumentException("Selection item name cannot be empty.", nameof(name)); + } + + _name = name; + } + + internal string GetName() => _name; +} + +internal static class MenuSelectionItemAttributeExtensions +{ + internal static IEnumerable GetSelectionMethods(this Type type) + { + var allPublicMethods = type.GetMethods(); + + var result = new List(); + + foreach (var method in allPublicMethods) + { + var attribute = method.GetCustomAttribute(); + if (attribute is null) continue; + + result.Add(new SelectionMethod(attribute.GetName(), method, attribute.PauseOnComplete)); + } + + return result; + } + + internal static MenuSelectionItemAttribute GetMenuSelectionItemAttribute(this MethodInfo method) + { + var attribute = method.GetCustomAttribute() + ?? throw new InvalidOperationException($"Method {method.Name} must have {nameof(MenuSelectionItemAttribute)}"); + + return attribute; + } +} diff --git a/src/BitzArt.Console/Extensions/AddConsoleMenuExtensions.cs b/src/BitzArt.Console/Extensions/AddConsoleMenuExtensions.cs index 324553c..96fba6e 100644 --- a/src/BitzArt.Console/Extensions/AddConsoleMenuExtensions.cs +++ b/src/BitzArt.Console/Extensions/AddConsoleMenuExtensions.cs @@ -8,53 +8,56 @@ public static class AddConsoleMenuExtensions /// /// Registers all console menus defined in the assembly containing this type in the /// - public static IServiceCollection AddConsoleMenusFromAssemblyContaining(this IServiceCollection services) - => services.AddConsoleMenusFromAssemblyContaining(typeof(TAssemblyPointer)); + public static IConsoleAppBuilder AddConsoleMenusFromAssemblyContaining(this IConsoleAppBuilder builder) + => builder.AddConsoleMenusFromAssemblyContaining(typeof(TAssemblyPointer)); /// /// Registers all console menus defined in the assembly containing this type in the /// - public static IServiceCollection AddConsoleMenusFromAssemblyContaining(this IServiceCollection services, Type type) - => services.AddConsoleMenusFromAssembly(type.Assembly); + public static IConsoleAppBuilder AddConsoleMenusFromAssemblyContaining(this IConsoleAppBuilder builder, Type type) + => builder.AddConsoleMenusFromAssembly(type.Assembly); /// /// Registers all console menus defined in the assembly in the /// - public static IServiceCollection AddConsoleMenusFromAssembly(this IServiceCollection services, Assembly assembly) + public static IConsoleAppBuilder AddConsoleMenusFromAssembly(this IConsoleAppBuilder builder, Assembly assembly) { var tools = assembly .DefinedTypes .Where(x => x.IsAbstract == false) - .Where(x => x.GetInterfaces().Contains(typeof(IConsoleMenu))); + .Where(x => x.GetCustomAttributes().Any()); - foreach (var tool in tools) services.AddConsoleMenu(tool); + foreach (var tool in tools) builder.AddConsoleMenu(tool); - return services; + return builder; } /// /// Registers a Console Menu of type in the /// - public static IServiceCollection AddConsoleMenu(this IServiceCollection services) - where TConsoleMenu : class, IConsoleMenu + public static IConsoleAppBuilder AddConsoleMenu(this IConsoleAppBuilder builder) + where TConsoleMenu : class { - services.AddTransient(); - services.AddTransient(); + builder.Services.AddTransient(); - return services; + var map = builder.MenuMap; + map.Add(typeof(TConsoleMenu)); + + return builder; } /// /// Registers a Console Menu of in the /// - public static IServiceCollection AddConsoleMenu(this IServiceCollection services, Type type) + public static IConsoleAppBuilder AddConsoleMenu(this IConsoleAppBuilder builder, Type type) { if (type is null) throw new ArgumentException($"{nameof(type)} must not be null"); - if (type.IsAssignableTo(typeof(IConsoleMenu)) == false) throw new ArgumentException($"{type.Name} is not assignable to IConsoleMenu"); - services.AddTransient(type); - services.AddTransient(x => (IConsoleMenu)x.GetRequiredService(type)); + builder.Services.AddTransient(type); + + var map = builder.MenuMap; + map.Add(type); - return services; + return builder; } } diff --git a/src/BitzArt.Console/Extensions/RunConsoleMenuExtension.cs b/src/BitzArt.Console/Extensions/RunConsoleMenuExtension.cs index 57e2a72..421640b 100644 --- a/src/BitzArt.Console/Extensions/RunConsoleMenuExtension.cs +++ b/src/BitzArt.Console/Extensions/RunConsoleMenuExtension.cs @@ -1,35 +1,53 @@ using Microsoft.Extensions.DependencyInjection; +using System; namespace BitzArt.Console; public static class RunConsoleMenuExtension { internal static async Task RunConsoleMenuAsync(this IServiceProvider serviceProvider) - where TConsoleMenu : IConsoleMenu + where TConsoleMenu : class { var menu = serviceProvider.GetRequiredService(); - AttachApp(menu, serviceProvider); + if (menu is not ConsoleMenu consoleMenu) return; + + consoleMenu.Populate(serviceProvider); - await menu.RunAsync(); + await consoleMenu.RunAsync(); } internal static async Task RunConsoleMenuAsync(this IServiceProvider serviceProvider, Type menuType) { - if (!menuType.IsAssignableTo(typeof(IConsoleMenu))) throw new ArgumentException($"Menu Type must implement {nameof(IConsoleMenu)}", nameof(menuType)); + var menu = serviceProvider.GetRequiredService(menuType); - var menu = (IConsoleMenu)serviceProvider.GetRequiredService(menuType); + if (menu is not ConsoleMenu consoleMenu) return; - AttachApp(menu, serviceProvider); + consoleMenu.Populate(serviceProvider); - await menu.RunAsync(); + await consoleMenu.RunAsync(); } - private static void AttachApp(this IConsoleMenu menu, IServiceProvider serviceProvider) + private static void Populate(this ConsoleMenu menu, IServiceProvider serviceProvider) { - if (menu is not ConsoleMenu consoleMenu) return; - var app = serviceProvider.GetService(); - consoleMenu.App = app; + menu.App = app; + + var appMenuAttribute = menu.GetType().GetAppMenuAttribute(); + menu.Title = appMenuAttribute!.GetTitle(); + menu.IsMainMenu = appMenuAttribute.IsMain; + + menu.PopulateSelectionFromMethods(); + } + + private static void PopulateSelectionFromMethods(this ConsoleMenu consoleMenu) + { + if (consoleMenu is not ConsoleSelectionMenu selectionMenu) return; + + var selections = consoleMenu.GetType().GetSelectionMethods(); + foreach (var selection in selections) + { + selectionMenu.AddSelection(selection.Name, () => selection.Method.Invoke(selectionMenu, null), selection.PauseOnComplete); + } } } diff --git a/src/BitzArt.Console/Interfaces/IConsoleAppBuilder.cs b/src/BitzArt.Console/Interfaces/IConsoleAppBuilder.cs new file mode 100644 index 0000000..3e712e6 --- /dev/null +++ b/src/BitzArt.Console/Interfaces/IConsoleAppBuilder.cs @@ -0,0 +1,13 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace BitzArt.Console; + +public interface IConsoleAppBuilder +{ + public IConfiguration Configuration { get; } + public IServiceCollection Services { get; } + internal MenuMap MenuMap { get; } + + public ConsoleApp Build(); +} \ No newline at end of file diff --git a/src/BitzArt.Console/Interfaces/IConsoleAppNavigationManager.cs b/src/BitzArt.Console/Interfaces/IConsoleAppNavigationManager.cs index a3874c7..7c8ffed 100644 --- a/src/BitzArt.Console/Interfaces/IConsoleAppNavigationManager.cs +++ b/src/BitzArt.Console/Interfaces/IConsoleAppNavigationManager.cs @@ -3,5 +3,6 @@ public interface IConsoleAppNavigationManager { Task NavigateAsync(Type menuType); - Task NavigateAsync() where T : IConsoleMenu; + Task NavigateAsync() where T : class; + Task NavigateToMainMenuAsync(); } \ No newline at end of file diff --git a/src/BitzArt.Console/Interfaces/IConsoleMenu.cs b/src/BitzArt.Console/Interfaces/IConsoleMenu.cs index e3a9465..79a2c36 100644 --- a/src/BitzArt.Console/Interfaces/IConsoleMenu.cs +++ b/src/BitzArt.Console/Interfaces/IConsoleMenu.cs @@ -1,6 +1 @@ namespace BitzArt.Console; - -public interface IConsoleMenu -{ - public Task RunAsync(); -} diff --git a/src/BitzArt.Console/Menus/ConsoleMenu.cs b/src/BitzArt.Console/Menus/ConsoleMenu.cs index 4e468d8..ea9b310 100644 --- a/src/BitzArt.Console/Menus/ConsoleMenu.cs +++ b/src/BitzArt.Console/Menus/ConsoleMenu.cs @@ -3,11 +3,14 @@ namespace BitzArt.Console; -public abstract class ConsoleMenu : IConsoleMenu +public abstract class ConsoleMenu { public ConsoleApp? App { get; set; } + public ConsoleMenu? Parent { get; set; } - public virtual string Title => "Menu"; + public string? Title { get; internal set; } + + public bool? IsMainMenu { get; internal set; } public Task RunAsync() { @@ -15,13 +18,13 @@ public Task RunAsync() return Task.CompletedTask; } - public virtual void Render() + protected virtual void Render() { AnsiConsole.Clear(); - AnsiConsoleMenu.WriteTitle(Title); + AnsiConsoleMenu.WriteTitle(Title!); } - public async Task RunAsync() where TConsoleMenu : IConsoleMenu + public async Task RunAsync() where TConsoleMenu : class { var navigationManager = App!.Services.GetRequiredService(); await navigationManager.NavigateAsync(); diff --git a/src/BitzArt.Console/Menus/ConsoleSelectionMenu.cs b/src/BitzArt.Console/Menus/ConsoleSelectionMenu.cs index 8849855..0885ebc 100644 --- a/src/BitzArt.Console/Menus/ConsoleSelectionMenu.cs +++ b/src/BitzArt.Console/Menus/ConsoleSelectionMenu.cs @@ -4,17 +4,10 @@ namespace BitzArt.Console; public abstract class ConsoleSelectionMenu : ConsoleMenu { - /// - /// Identifies whether this is a main menu of the app or not. - /// - protected virtual bool IsMainMenu => false; - - public override string Title => "Selection Menu"; - protected virtual List SelectionItems { get; set; } = []; public void AddSubmenu(string? selectionName = null) - where TMenu : IConsoleMenu + where TMenu : class { selectionName ??= typeof(TMenu).Name; AddSelection(selectionName, async () => await RunAsync()); @@ -25,31 +18,32 @@ public void AddSelection(string name, Action? action = null, bool pauseOnComplet SelectionItems.Add(new ConsoleSelectionMenuItem(name, action, pauseOnComplete)); } - public override void Render() + protected override void Render() { - AnsiConsole.Clear(); - - var selectionPrompt = new SelectionPrompt().Title($"[green]{Title}[/]"); + while(true) + { + AnsiConsole.Clear(); - selectionPrompt.AddChoices(SelectionItems); + var selectionPrompt = new SelectionPrompt().Title($"[green]{Title}[/]"); - var backSelectionOption = IsMainMenu ? ConsoleSelectionMenuItem.ExitItem : ConsoleSelectionMenuItem.BackItem; - selectionPrompt.AddChoice(backSelectionOption); + selectionPrompt.AddChoices(SelectionItems); - var selected = selectionPrompt.Show(AnsiConsole.Console); + var backSelectionOption = IsMainMenu!.Value ? ConsoleSelectionMenuItem.ExitItem : ConsoleSelectionMenuItem.BackItem; + selectionPrompt.AddChoice(backSelectionOption); - if (selected.IsExit) - { - OnExit(); - return; - } + var selected = selectionPrompt.Show(AnsiConsole.Console); - OnBeforeSelectionInvoke(selected); - InvokeSelection(selected); - OnSelection(selected); - OnAfterSelectionInvoked(selected); + if (selected.IsExit) + { + OnExit(); + return; + } - Render(); + OnBeforeSelectionInvoke(selected); + InvokeSelection(selected); + OnSelection(selected); + OnAfterSelectionInvoked(selected); + } } public virtual void OnBeforeSelectionInvoke(ConsoleSelectionMenuItem selection) @@ -67,11 +61,13 @@ public virtual void OnSelection(ConsoleSelectionMenuItem selection) public virtual void OnAfterSelectionInvoked(ConsoleSelectionMenuItem selection) { - if (selection.PauseOnComplete) - { - AnsiConsole.WriteLine("Press any key to continue..."); - System.Console.ReadKey(); - } + if (selection.PauseOnComplete) Pause(); + } + + protected static void Pause() + { + AnsiConsole.WriteLine("Press any key to continue..."); + System.Console.ReadKey(); } public virtual void OnExit() diff --git a/src/BitzArt.Console/Models/MenuMapItem.cs b/src/BitzArt.Console/Models/MenuMapItem.cs new file mode 100644 index 0000000..1731f39 --- /dev/null +++ b/src/BitzArt.Console/Models/MenuMapItem.cs @@ -0,0 +1,3 @@ +namespace BitzArt.Console; + +internal record MenuMapItem(bool IsMainMenu, string Title, Type MenuType); diff --git a/src/BitzArt.Console/Models/SelectionMethod.cs b/src/BitzArt.Console/Models/SelectionMethod.cs new file mode 100644 index 0000000..8a38cc3 --- /dev/null +++ b/src/BitzArt.Console/Models/SelectionMethod.cs @@ -0,0 +1,5 @@ +using System.Reflection; + +namespace BitzArt.Console; + +internal record SelectionMethod(string Name, MethodInfo Method, bool PauseOnComplete); diff --git a/src/BitzArt.Console/Services/ConsoleAppNavigationManager.cs b/src/BitzArt.Console/Services/ConsoleAppNavigationManager.cs index 8a0a06d..726f7d8 100644 --- a/src/BitzArt.Console/Services/ConsoleAppNavigationManager.cs +++ b/src/BitzArt.Console/Services/ConsoleAppNavigationManager.cs @@ -1,10 +1,19 @@ -namespace BitzArt.Console; +using Microsoft.Extensions.DependencyInjection; + +namespace BitzArt.Console; internal class ConsoleAppNavigationManager(IServiceProvider serviceProvider) : IConsoleAppNavigationManager { private readonly IServiceProvider _serviceProvider = serviceProvider; - public async Task NavigateAsync() where T : IConsoleMenu + public async Task NavigateToMainMenuAsync() + { + var map = _serviceProvider.GetRequiredService(); + var mainMenu = map.GetMainMenuItem(); + await _serviceProvider.RunConsoleMenuAsync(mainMenu.MenuType); + } + + public async Task NavigateAsync() where T : class { await _serviceProvider.RunConsoleMenuAsync(); } diff --git a/src/BitzArt.Console/Services/MenuMap.cs b/src/BitzArt.Console/Services/MenuMap.cs new file mode 100644 index 0000000..242eb40 --- /dev/null +++ b/src/BitzArt.Console/Services/MenuMap.cs @@ -0,0 +1,32 @@ +namespace BitzArt.Console; + +internal class MenuMap +{ + private readonly List _menus = []; + + public void Add(Type menuType) + { + var attribute = menuType.GetAppMenuAttribute(); + + var item = new MenuMapItem(attribute.IsMain, attribute.GetTitle(), menuType); + _menus.Add(item); + } + + public IEnumerable GetItems() + { + return _menus; + } + + public IEnumerable GetMainMenuItems() + { + return _menus.Where(x => x.IsMainMenu == true); + } + + public MenuMapItem GetMainMenuItem() + { + var items = GetMainMenuItems().ToList(); + if (items.Count == 0) throw new InvalidOperationException("There is no main menu registered."); + if (items.Count > 1) throw new InvalidOperationException("There are more than one main menu registered."); + return items.First(); + } +} diff --git a/tests/BitzArt.Console.Tests/Menus/TestMenu1.cs b/tests/BitzArt.Console.Tests/Menus/TestMenu1.cs deleted file mode 100644 index 16d8c64..0000000 --- a/tests/BitzArt.Console.Tests/Menus/TestMenu1.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Spectre.Console; - -namespace BitzArt.Console; - -public class TestMenu1 : IConsoleMenu -{ - public Task RunAsync() - { - AnsiConsole.WriteLine("Test Menu 1"); - - return Task.CompletedTask; - } -} diff --git a/tests/BitzArt.Console.Tests/Menus/TestMenu2.cs b/tests/BitzArt.Console.Tests/Menus/TestMenu2.cs deleted file mode 100644 index 0b4c987..0000000 --- a/tests/BitzArt.Console.Tests/Menus/TestMenu2.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Spectre.Console; - -namespace BitzArt.Console; - -public class TestMenu2 : IConsoleMenu -{ - public Task RunAsync() - { - AnsiConsole.WriteLine("Test Menu 2"); - - return Task.CompletedTask; - } -} diff --git a/tests/BitzArt.Console.Tests/Tests/ServiceInjectionTests.cs b/tests/BitzArt.Console.Tests/Tests/ServiceInjectionTests.cs deleted file mode 100644 index 2478049..0000000 --- a/tests/BitzArt.Console.Tests/Tests/ServiceInjectionTests.cs +++ /dev/null @@ -1,109 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; - -namespace BitzArt.Console.Tests; - -public class ServiceInjectionTests -{ - [Fact] - public void AddConsoleTool_TestTool1_Adds() - { - var services = new ServiceCollection(); - - services.AddConsoleMenu(); - - var provider = services.BuildServiceProvider(); - - var tools = provider.GetServices(); - Assert.Single(tools); - - var toolByInterface = provider.GetService(); - Assert.NotNull(toolByInterface); - Assert.True(toolByInterface is TestMenu1); - - var toolByType = provider.GetService(); - Assert.NotNull(toolByType); - Assert.True(toolByType is IConsoleMenu); - } - - [Fact] - public void AddConsoleTool_TwoGenericCalls_AddsBoth() - { - var services = new ServiceCollection(); - - services.AddConsoleMenu(); - services.AddConsoleMenu(); - - var provider = services.BuildServiceProvider(); - - var tools = provider.GetServices(); - - Assert.Equal(2, tools.Count()); - Assert.Contains(tools, x => x.GetType() == typeof(TestMenu1)); - Assert.Contains(tools, x => x.GetType() == typeof(TestMenu2)); - } - - [Fact] - public void AddConsoleTool_TwoReflectionCalls_AddsBoth() - { - var services = new ServiceCollection(); - - services.AddConsoleMenu(typeof(TestMenu1)); - services.AddConsoleMenu(typeof(TestMenu2)); - - var provider = services.BuildServiceProvider(); - - var tools = provider.GetServices(); - - Assert.Equal(2, tools.Count()); - Assert.Contains(tools, x => x.GetType() == typeof(TestMenu1)); - Assert.Contains(tools, x => x.GetType() == typeof(TestMenu2)); - } - - [Fact] - public void AddConsoleToolsFromAssembly_ThisAssembly_AddsBoth() - { - var services = new ServiceCollection(); - - services.AddConsoleMenusFromAssembly(typeof(ServiceInjectionTests).Assembly); - - var provider = services.BuildServiceProvider(); - - var tools = provider.GetServices(); - - Assert.Equal(2, tools.Count()); - Assert.Contains(tools, x => x.GetType() == typeof(TestMenu1)); - Assert.Contains(tools, x => x.GetType() == typeof(TestMenu2)); - } - - [Fact] - public void AddConsoleToolsFromAssemblyContaining_Typeof_AddsBoth() - { - var services = new ServiceCollection(); - - services.AddConsoleMenusFromAssemblyContaining(typeof(ServiceInjectionTests)); - - var provider = services.BuildServiceProvider(); - - var tools = provider.GetServices(); - - Assert.Equal(2, tools.Count()); - Assert.Contains(tools, x => x.GetType() == typeof(TestMenu1)); - Assert.Contains(tools, x => x.GetType() == typeof(TestMenu2)); - } - - [Fact] - public void AddConsoleToolsFromAssemblyContaining_Generic_AddsBoth() - { - var services = new ServiceCollection(); - - services.AddConsoleMenusFromAssemblyContaining(); - - var provider = services.BuildServiceProvider(); - - var tools = provider.GetServices(); - - Assert.Equal(2, tools.Count()); - Assert.Contains(tools, x => x.GetType() == typeof(TestMenu1)); - Assert.Contains(tools, x => x.GetType() == typeof(TestMenu2)); - } -}