diff --git a/SharedLibraryCore/Helpers/ParsedInputResult.cs b/SharedLibraryCore/Helpers/ParsedInputResult.cs new file mode 100644 index 00000000..3c590f59 --- /dev/null +++ b/SharedLibraryCore/Helpers/ParsedInputResult.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace SharedLibraryCore.Helpers; + +public class ParsedInputResult +{ + public TResult? Result { get; set; } + public string? RawInput { get; set; } + public List ErrorMessages { get; set; } = []; + + public ParsedInputResult WithError(string errorMessage) + { + ErrorMessages.Add(errorMessage); + return this; + } +} diff --git a/SharedLibraryCore/Utilities.cs b/SharedLibraryCore/Utilities.cs index 4ac41ac8..ee47a773 100644 --- a/SharedLibraryCore/Utilities.cs +++ b/SharedLibraryCore/Utilities.cs @@ -1375,28 +1375,32 @@ public static void ExecuteAfterDelay(int delayMs, Func public static void ExecuteAfterDelay(this Func action, int delayMs, CancellationToken token = default) => ExecuteAfterDelay(delayMs, action, token); - public static async Task PromptClientInput(this EFClient client, string[] prompt, Func> validator, - CancellationToken token = default) + public static async Task> PromptClientInput(this EFClient client, string[] prompts, + Func>> parser, string tokenExpiredMessage, CancellationToken token = default) { var clientResponse = new ManualResetEventSlim(false); - string response = null; + ParsedInputResult? response = null; try { IGameEventSubscriptions.ClientMessaged += OnResponse; - await client.TellAsync(prompt, token); + await client.TellAsync(prompts, token); using var tokenSource = new CancellationTokenSource(DefaultCommandTimeout); using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(tokenSource.Token, token); - clientResponse.Wait(linkedTokenSource.Token); + try + { + clientResponse.Wait(linkedTokenSource.Token); + } + catch (OperationCanceledException) + { + await client.TellAsync([tokenExpiredMessage], token); + return new ParsedInputResult { ErrorMessages = [tokenExpiredMessage] }; + } return response; } - catch (OperationCanceledException) - { - return null; - } finally { IGameEventSubscriptions.ClientMessaged -= OnResponse; @@ -1410,16 +1414,17 @@ async Task OnResponse(ClientMessageEvent messageEvent, CancellationToken cancell return; } - response = messageEvent.Message; + response = await parser(messageEvent.Message); + response.RawInput = messageEvent.Message; - if (await validator(response)) + if (response.ErrorMessages.Count is 0) { // ReSharper disable once AccessToDisposedClosure clientResponse.Set(); } else { - await client.TellAsync(prompt, cancellationToken); + await client.TellAsync(response.ErrorMessages.Concat(prompts), cancellationToken); } } }