diff --git a/src/SampSharp.OpenMp.Entities.Commands/Console/DefaultConsoleCommandMessageService.cs b/src/SampSharp.OpenMp.Entities.Commands/Console/DefaultConsoleCommandMessageService.cs index 150d6843..88902372 100644 --- a/src/SampSharp.OpenMp.Entities.Commands/Console/DefaultConsoleCommandMessageService.cs +++ b/src/SampSharp.OpenMp.Entities.Commands/Console/DefaultConsoleCommandMessageService.cs @@ -1,3 +1,5 @@ +using Microsoft.Extensions.Options; + namespace SampSharp.Entities.SAMP.Commands; /// @@ -7,16 +9,20 @@ namespace SampSharp.Entities.SAMP.Commands; internal class DefaultConsoleCommandMessageService : IConsoleCommandMessageService { private readonly ICommandTextFormatter _formatter; + private readonly ConsoleCommandServiceOptions _options; /// /// Initializes a new instance of the class with the specified command text formatter. /// /// A formatter used to format command text. - public DefaultConsoleCommandMessageService(ICommandTextFormatter formatter) + /// The command service options. + public DefaultConsoleCommandMessageService(ICommandTextFormatter formatter, IOptions options) { ArgumentNullException.ThrowIfNull(formatter); + ArgumentNullException.ThrowIfNull(options); _formatter = formatter; + _options = options.Value; } /// @@ -43,11 +49,11 @@ public virtual bool SendUsage(ConsoleCommandDispatchContext context, IReadOnlyLi var text = _formatter.FormatCommandUsage(commandName, group, overload.ParsedParameters, includeSlash: false); - context.SendMessage($"Usage: {text}"); + context.SendMessage($"{_options.UsageMessagePrefix} {text}"); } else { - context.SendMessage("Usage:"); + context.SendMessage(_options.UsageMessagePrefix); foreach (var overload in overloads) { // If usedCommandName is provided (e.g., an alias), use it as the complete path without the group diff --git a/src/SampSharp.OpenMp.Entities.Commands/Options/CommandServiceOptions.cs b/src/SampSharp.OpenMp.Entities.Commands/Options/CommandServiceOptions.cs index 4dab3537..a400f9a2 100644 --- a/src/SampSharp.OpenMp.Entities.Commands/Options/CommandServiceOptions.cs +++ b/src/SampSharp.OpenMp.Entities.Commands/Options/CommandServiceOptions.cs @@ -10,4 +10,12 @@ public class CommandServiceOptions /// Defaults to . /// public StringComparison StringComparison { get; set; } = StringComparison.OrdinalIgnoreCase; + + /// + /// Gets or sets the prefix displayed before a command usage message. + /// For example, when set to "Syntax:", a usage message may be displayed as: + /// "Syntax: /pm [targetId] [reason]". + /// Defaults to "Usage:". + /// + public string UsageMessagePrefix { get; set; } = "Usage:"; } \ No newline at end of file diff --git a/src/SampSharp.OpenMp.Entities.Commands/Options/PlayerCommandServiceOptions.cs b/src/SampSharp.OpenMp.Entities.Commands/Options/PlayerCommandServiceOptions.cs index aded42f4..41941880 100644 --- a/src/SampSharp.OpenMp.Entities.Commands/Options/PlayerCommandServiceOptions.cs +++ b/src/SampSharp.OpenMp.Entities.Commands/Options/PlayerCommandServiceOptions.cs @@ -5,4 +5,9 @@ namespace SampSharp.Entities.SAMP.Commands; /// public class PlayerCommandServiceOptions : CommandServiceOptions { + /// + /// Gets or sets the color used when displaying command usage messages. + /// Defaults to . + /// + public Color UsageMessageColor { get; set; } = Color.White; } \ No newline at end of file diff --git a/src/SampSharp.OpenMp.Entities.Commands/Player/DefaultPlayerCommandMessageService.cs b/src/SampSharp.OpenMp.Entities.Commands/Player/DefaultPlayerCommandMessageService.cs index 9e1bf5ad..43753e79 100644 --- a/src/SampSharp.OpenMp.Entities.Commands/Player/DefaultPlayerCommandMessageService.cs +++ b/src/SampSharp.OpenMp.Entities.Commands/Player/DefaultPlayerCommandMessageService.cs @@ -1,3 +1,5 @@ +using Microsoft.Extensions.Options; + namespace SampSharp.Entities.SAMP.Commands; /// @@ -7,16 +9,20 @@ namespace SampSharp.Entities.SAMP.Commands; public class DefaultPlayerCommandMessageService : IPlayerCommandMessageService { private readonly ICommandTextFormatter _formatter; + private readonly PlayerCommandServiceOptions _options; /// /// Initializes a new instance of the class with the specified command text formatter. /// /// A formatter used to format command text. - public DefaultPlayerCommandMessageService(ICommandTextFormatter formatter) + /// The command service options. + public DefaultPlayerCommandMessageService(ICommandTextFormatter formatter, IOptions options) { ArgumentNullException.ThrowIfNull(formatter); + ArgumentNullException.ThrowIfNull(options); _formatter = formatter; + _options = options.Value; } /// @@ -48,11 +54,11 @@ public virtual void SendUsage(Player player, IReadOnlyList ov } var text = _formatter.FormatCommandUsage(commandName, group, overload.ParsedParameters, includeSlash: true); - messages.Add($"Usage: {text}"); + messages.Add($"{_options.UsageMessagePrefix} {text}"); } else { - messages.Add("Usage:"); + messages.Add(_options.UsageMessagePrefix); foreach (var overload in overloads) { // If usedCommandName is provided (e.g., an alias), use it as the complete path without the group @@ -78,7 +84,7 @@ public virtual void SendUsage(Player player, IReadOnlyList ov foreach (var message in messages) { - player.SendClientMessage(message); + player.SendClientMessage(_options.UsageMessageColor, message); } } diff --git a/test/SampSharp.OpenMp.Entities.Commands.Tests/Console/DefaultConsoleCommandMessageServiceTests.cs b/test/SampSharp.OpenMp.Entities.Commands.Tests/Console/DefaultConsoleCommandMessageServiceTests.cs index cd331c46..c83d2a30 100644 --- a/test/SampSharp.OpenMp.Entities.Commands.Tests/Console/DefaultConsoleCommandMessageServiceTests.cs +++ b/test/SampSharp.OpenMp.Entities.Commands.Tests/Console/DefaultConsoleCommandMessageServiceTests.cs @@ -1,4 +1,5 @@ using System.Reflection; +using Microsoft.Extensions.Options; using Moq; using SampSharp.Entities; using SampSharp.Entities.SAMP.Commands; @@ -35,7 +36,7 @@ private void DummyMethod() { } [Fact] public void Constructor_NullFormatter_ThrowsArgumentNullException() { - Should.Throw(() => new DefaultConsoleCommandMessageService(null!)); + Should.Throw(() => new DefaultConsoleCommandMessageService(null!, null!)); } [Fact] @@ -44,10 +45,13 @@ public void SendUsage_SingleOverload_SendsFormattedMessage() var messages = new List(); var context = new ConsoleCommandDispatchContext(null, messages.Add); var formatterMock = new Mock(); + var optionsMock = new Mock>(); formatterMock.Setup(f => f.FormatCommandUsage(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns("test "); + optionsMock.Setup(f => f.Value) + .Returns(new ConsoleCommandServiceOptions()); - var service = new DefaultConsoleCommandMessageService(formatterMock.Object); + var service = new DefaultConsoleCommandMessageService(formatterMock.Object, optionsMock.Object); var overload = CreateDefinition("test"); service.SendUsage(context, new[] { overload }); @@ -62,10 +66,13 @@ public void SendUsage_MultipleOverloads_SendsHeaderAndEachOverload() var messages = new List(); var context = new ConsoleCommandDispatchContext(null, messages.Add); var formatterMock = new Mock(); + var optionsMock = new Mock>(); formatterMock.Setup(f => f.FormatCommandUsage(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns("formatted"); + optionsMock.Setup(f => f.Value) + .Returns(new ConsoleCommandServiceOptions()); - var service = new DefaultConsoleCommandMessageService(formatterMock.Object); + var service = new DefaultConsoleCommandMessageService(formatterMock.Object, optionsMock.Object); var overload1 = CreateDefinition("test"); var overload2 = CreateDefinition("test"); @@ -82,11 +89,14 @@ public void SendUsage_WithUsedCommandName_UsesAliasName() string? capturedName = null; var context = new ConsoleCommandDispatchContext(null, _ => { }); var formatterMock = new Mock(); + var optionsMock = new Mock>(); formatterMock.Setup(f => f.FormatCommandUsage(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Callback((string name, string? group, CommandParameterInfo[] _, bool _) => capturedName = name) .Returns("formatted"); + optionsMock.Setup(f => f.Value) + .Returns(new ConsoleCommandServiceOptions()); - var service = new DefaultConsoleCommandMessageService(formatterMock.Object); + var service = new DefaultConsoleCommandMessageService(formatterMock.Object, optionsMock.Object); var overload = CreateDefinition("message"); service.SendUsage(context, new[] { overload }, usedCommandName: "pm"); @@ -100,11 +110,14 @@ public void SendUsage_SingleOverload_DoesNotIncludeSlash() bool? capturedIncludeSlash = null; var context = new ConsoleCommandDispatchContext(null, _ => { }); var formatterMock = new Mock(); + var optionsMock = new Mock>(); formatterMock.Setup(f => f.FormatCommandUsage(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Callback((string _, string? _, CommandParameterInfo[] _, bool includeSlash) => capturedIncludeSlash = includeSlash) .Returns("formatted"); + optionsMock.Setup(f => f.Value) + .Returns(new ConsoleCommandServiceOptions()); - var service = new DefaultConsoleCommandMessageService(formatterMock.Object); + var service = new DefaultConsoleCommandMessageService(formatterMock.Object, optionsMock.Object); var overload = CreateDefinition("test"); service.SendUsage(context, new[] { overload }); @@ -117,10 +130,13 @@ public void SendUsage_ReturnsTrue() { var context = new ConsoleCommandDispatchContext(null, _ => { }); var formatterMock = new Mock(); + var optionsMock = new Mock>(); formatterMock.Setup(f => f.FormatCommandUsage(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns("formatted"); + optionsMock.Setup(f => f.Value) + .Returns(new ConsoleCommandServiceOptions()); - var service = new DefaultConsoleCommandMessageService(formatterMock.Object); + var service = new DefaultConsoleCommandMessageService(formatterMock.Object, optionsMock.Object); var overload = CreateDefinition("test"); var result = service.SendUsage(context, new[] { overload });