diff --git a/BusyList/Commands/Commands.cs b/BusyList/Commands/Commands.cs index caccea7..2c7665f 100644 --- a/BusyList/Commands/Commands.cs +++ b/BusyList/Commands/Commands.cs @@ -1,4 +1,6 @@ -namespace BusyList.Commands +using System; + +namespace BusyList.Commands { public record Command; @@ -7,4 +9,6 @@ public record NextCommand() : Command; public record AddCommand(string Description, PriorityEnum Priority = PriorityEnum.Normal) : Command; public record DeleteCommand(int Id) : Command; public record DoneCommand(int Id) : Command; + public record HelpCommand(string name = null) : Command; } + diff --git a/BusyList/Handlers/AddHandler.cs b/BusyList/Handlers/AddHandler.cs index d717270..df3d16f 100644 --- a/BusyList/Handlers/AddHandler.cs +++ b/BusyList/Handlers/AddHandler.cs @@ -1,9 +1,11 @@ using BusyList.Commands; +using BusyList.HelpSystem; using System; namespace BusyList.Handlers { + [HelpAttribute("add", "Add a new task with the passed description", "add [Description]")] public class AddHandler : IHandler { private readonly ITaskRepository _taskRepository; @@ -12,6 +14,7 @@ public AddHandler(ITaskRepository taskRepository) { _taskRepository = taskRepository; } + public void Run(AddCommand command) { var item = new AddTaskData(command.Description, command.Priority); @@ -20,6 +23,5 @@ public void Run(AddCommand command) Console.WriteLine($"Added {currentTask.Print()}"); } - } } diff --git a/BusyList/Handlers/DeleteHandler.cs b/BusyList/Handlers/DeleteHandler.cs index 6485a55..4b69c7b 100644 --- a/BusyList/Handlers/DeleteHandler.cs +++ b/BusyList/Handlers/DeleteHandler.cs @@ -1,8 +1,10 @@ using BusyList.Commands; +using BusyList.HelpSystem; using System; namespace BusyList.Handlers { + [HelpAttribute("delete", "Delete the task with the passed id", "[Id] delete")] public class DeleteHandler : IHandler { private readonly ITaskRepository _taskRepository; @@ -11,7 +13,7 @@ public DeleteHandler(ITaskRepository taskRepository) { _taskRepository = taskRepository; } - + public void Run(DeleteCommand command) { var selectedTask = _taskRepository.GetTaskById(command.Id); @@ -19,4 +21,4 @@ public void Run(DeleteCommand command) Console.WriteLine($"Task with id {command.Id} has been deleted."); } } -} \ No newline at end of file +} diff --git a/BusyList/Handlers/DoneHandler.cs b/BusyList/Handlers/DoneHandler.cs index 3828f32..73e2849 100644 --- a/BusyList/Handlers/DoneHandler.cs +++ b/BusyList/Handlers/DoneHandler.cs @@ -1,8 +1,10 @@ using BusyList.Commands; +using BusyList.HelpSystem; using System; namespace BusyList.Handlers { + [HelpAttribute("done", "Mark the task with the given id as done", "[Id] done")] public class DoneHandler : IHandler { private readonly ITaskRepository _taskRepository; diff --git a/BusyList/Handlers/HelpHandler.cs b/BusyList/Handlers/HelpHandler.cs new file mode 100644 index 0000000..059d827 --- /dev/null +++ b/BusyList/Handlers/HelpHandler.cs @@ -0,0 +1,51 @@ +using BusyList.Commands; +using BusyList.HelpSystem; +using System; + +namespace BusyList.Handlers +{ + public class HelpHandler : IHandler + { + private readonly HelpProvider _helpProvider; + + public HelpHandler(HelpProvider helpProvider) + { + _helpProvider = helpProvider; + } + + public void Help() + { + var helpTexts = _helpProvider.GetAllHelpText(); + + foreach (var (name, description) in helpTexts) + { + Console.WriteLine($"{name}: {description}"); + } + + Console.WriteLine("For further information type help [keyword] e.g help add"); + } + + public void Run(HelpCommand command) + { + if (command.name == null) + { + Help(); + + return; + } + + var (description, syntax) = _helpProvider.GetHelpText(command.name); + + if (description != null && syntax != null) + { + Console.WriteLine($"Command: {command.name.ToLowerInvariant()}"); + Console.WriteLine($"Description: {description}"); + Console.WriteLine($"Syntax: {syntax}"); + } + else + { + Console.WriteLine($"Help for the command '{command.name.ToLowerInvariant()}' does not exists"); + } + } + } +} diff --git a/BusyList/Handlers/IHandler.cs b/BusyList/Handlers/IHandler.cs index 9412982..b6e5c4a 100644 --- a/BusyList/Handlers/IHandler.cs +++ b/BusyList/Handlers/IHandler.cs @@ -2,8 +2,8 @@ namespace BusyList.Handlers { - internal interface IHandler where TCommand : Command + public interface IHandler where TCommand : Command { void Run(TCommand command); } -} \ No newline at end of file +} diff --git a/BusyList/Handlers/NextHandler.cs b/BusyList/Handlers/NextHandler.cs index 1aa39c5..46bc0d7 100644 --- a/BusyList/Handlers/NextHandler.cs +++ b/BusyList/Handlers/NextHandler.cs @@ -1,8 +1,11 @@ using BusyList.Commands; +using BusyList.HelpSystem; using System; namespace BusyList.Handlers { + + [HelpAttribute("next", "Lists all tasks", "next")] public class NextHandler : IHandler { private readonly ITaskRepository _taskRepository; diff --git a/BusyList/Handlers/ReadHandler.cs b/BusyList/Handlers/ReadHandler.cs index 9527c59..50c5764 100644 --- a/BusyList/Handlers/ReadHandler.cs +++ b/BusyList/Handlers/ReadHandler.cs @@ -1,8 +1,10 @@ using BusyList.Commands; +using BusyList.HelpSystem; using System; namespace BusyList.Handlers { + [HelpAttribute("read", "Print the id, description and status of the task with the passed id", "[Id]")] public class ReadHandler : IHandler { private readonly ITaskRepository _taskRepository; @@ -25,4 +27,4 @@ public void Run(ReadCommand command) Console.WriteLine(task.Print()); } } -} \ No newline at end of file +} diff --git a/BusyList/HelpSystem/HelpAttribute.cs b/BusyList/HelpSystem/HelpAttribute.cs new file mode 100644 index 0000000..8d5a53a --- /dev/null +++ b/BusyList/HelpSystem/HelpAttribute.cs @@ -0,0 +1,19 @@ +using System; + +namespace BusyList.HelpSystem +{ + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] + public class HelpAttribute : Attribute + { + public string Name { get; } + public string Description { get; } + public string Syntax { get; } + + public HelpAttribute(string name, string description, string syntax) + { + Name = name; + Description = description; + Syntax = syntax; + } + } +} diff --git a/BusyList/HelpSystem/HelpProvider.cs b/BusyList/HelpSystem/HelpProvider.cs new file mode 100644 index 0000000..087c7b4 --- /dev/null +++ b/BusyList/HelpSystem/HelpProvider.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace BusyList.HelpSystem +{ + public class HelpProvider + { + private readonly Dictionary _helpText = new(); + + public HelpProvider() + { + _helpText = Assembly.GetExecutingAssembly().GetExportedTypes() + .Where(x => x.IsDefined(typeof(HelpAttribute))) + .Select(x => x.GetCustomAttribute()) + .ToDictionary(x => x.Name.ToLowerInvariant(), x => (x.Description, x.Syntax)); + } + + public (string, string)[] GetAllHelpText() => _helpText.Select(x => (x.Key.ToLowerInvariant(), x.Value.Item1)).ToArray(); + + public (string, string) GetHelpText(string key) + { + if(_helpText.TryGetValue(key.ToLowerInvariant(), out var value)) + { + return value; + } + + return (null, null); + } + } +} diff --git a/BusyList/Parsing/CommandGrammar.cs b/BusyList/Parsing/CommandGrammar.cs index 76a8293..0dd9405 100644 --- a/BusyList/Parsing/CommandGrammar.cs +++ b/BusyList/Parsing/CommandGrammar.cs @@ -1,5 +1,8 @@ -using BusyList.Commands; +using BusyList.Commands; using Sprache; +using System; +using System.Linq; + namespace BusyList.Parsing { @@ -8,6 +11,9 @@ public static class CommandGrammar private static readonly Parser _keywordAdd = Parse.IgnoreCase("add").Text(); + private static readonly Parser _keywordHelp = + Parse.IgnoreCase("help").Text(); + private static readonly Parser _keywordDelete = Parse.IgnoreCase("delete") .Or(Parse.IgnoreCase("del")) @@ -78,6 +84,16 @@ from _ in Parse.WhiteSpace from keyword in _keywordDone select new DoneCommand(id); + private static readonly Parser _helpCommand = + from keyword in _keywordHelp + select new HelpCommand(); + + private static readonly Parser _helpCommandSpecific = + from keyword in _keywordHelp + from _ in Parse.WhiteSpace + from name in Parse.AnyChar.AtLeastOnce().Text() + select new HelpCommand(name); + public static readonly Parser Source = _deleteCommand .Or(_nextCommand) @@ -85,6 +101,8 @@ from keyword in _keywordDone .Or(_readCommand) .Or(_addCommandWithPriority) .Or(_addCommand) + .Or(_helpCommandSpecific) + .Or(_helpCommand) .End(); } } diff --git a/BusyList/Program.cs b/BusyList/Program.cs index 966bdcb..1ec9598 100644 --- a/BusyList/Program.cs +++ b/BusyList/Program.cs @@ -1,9 +1,11 @@ using BusyList.Commands; using BusyList.Handlers; +using BusyList.HelpSystem; using BusyList.Parsing; using Microsoft.Extensions.DependencyInjection; using Sprache; using System; +using System.Reflection; namespace BusyList { @@ -65,6 +67,8 @@ private static IServiceCollection ConfigureServices() services.AddTransient, DoneHandler>(); services.AddTransient, NextHandler>(); services.AddTransient, ReadHandler>(); + services.AddTransient, HelpHandler>(); + services.AddSingleton(); return services; } @@ -88,6 +92,9 @@ private static void HandleCommand(ServiceProvider provider, Command command) case ReadCommand read: provider.GetRequiredService>().Run(read); break; + case HelpCommand help: + provider.GetRequiredService>().Run(help); + break; default: throw new Exception($"Unknown command type {command.GetType().FullName} sent to HandleCommand!"); }