using Microsoft.Extensions.Logging; using System.Text; namespace DigitalData.Core.DTO { /// /// Provides extension methods for data transfer objects (DTOs). /// public static class DTOExtensions { /// /// Adds a single message to the result, if not null. /// /// The type of the result. /// The result to add the message to. /// The message to add. /// The updated result. public static T Message(this T result, string? message) where T : Result { if(message is not null) result.Messages.Add(message); return result; } internal static IEnumerable FilterNull(this IEnumerable list) { foreach (var item in list) if(item is not null) yield return item; } /// /// Adds multiple messages to the result, after removing nulls. /// /// The type of the result. /// The result to add the messages to. /// The messages to add. /// The updated result. public static T Message(this T result, params string?[] messages) where T : Result { result.Messages.AddRange(messages.FilterNull()); return result; } /// /// Adds a collection of messages to the result. /// /// The type of the result. /// The result to add the messages to. /// The collection of messages to add. /// The updated result. public static T Message(this T result, IEnumerable messages) where T : Result { result.Messages.AddRange(messages.FilterNull()); return result; } /// /// Adds a notice to the result. /// /// The type of the result. /// The result to add the notice to. /// The notice to add. /// The updated result. public static T Notice(this T result, Notice notice) where T : Result { result.Notices.Add(notice); return result; } /// /// Adds a collection of notices to the result. /// /// The type of the result. /// The result to add the notices to. /// The collection of notices to add. /// The updated result. public static T Notice(this T result, IEnumerable notices) where T : Result { result.Notices.AddRange(notices); return result; } /// /// Adds notices with a specific log level and flags to the result. /// /// The type of the result. /// The result to add the notices to. /// The log level of the notices. /// The flags associated with the notices. /// The updated result. public static T Notice(this T result, LogLevel level, params Enum[] flags) where T : Result { var notices = flags.Select(flag => new Notice() { Flag = flag, Level = level }); result.Notices.AddRange(notices); return result; } /// /// Adds a notice with a specific log level, flag, and messages to the result. /// /// The type of the result. /// The result to add the notice to. /// The log level of the notice. /// The flag associated with the notice. /// The messages to add to the notice. /// The updated result. public static T Notice(this T result, LogLevel level, Enum flag, params string?[] messages) where T : Result { result.Notices.Add(new Notice() { Flag = flag, Level = level, Messages = messages.FilterNull().ToList() }); return result; } /// /// Adds a notice with a specific log level and messages to the result. /// /// The type of the result. /// The result to add the notice to. /// The log level of the notice. /// The messages to add to the notice. /// The updated result. public static T Notice(this T result, LogLevel level, params string[] messages) where T : Result { result.Notices.Add(new Notice() { Flag = null, Level = level, Messages = messages.FilterNull().ToList() }); return result; } /// /// Checks if any notice has the specified flag. /// /// The collection of notices to check. /// The flag to check for. /// True if any notice has the specified flag; otherwise, false. public static bool HasFlag(this IEnumerable notices, Enum flag) => notices.Any(n => n.Flag?.ToString() == flag.ToString()); /// /// Checks if any notice has any of the specified flags. /// /// The collection of notices to check. /// The flags to check for. /// True if any notice has any of the specified flags; otherwise, false. public static bool HasAnyFlag(this IEnumerable notices, params Enum[] flags) => flags.Any(f => notices.HasFlag(f)); /// /// Executes a function based on the success or failure of the task result, /// without using result data. /// /// The type of the return value. /// The task returning a result to evaluate. /// The function to execute if the result is successful. /// The function to execute if the result is a failure. /// The result of the executed function. public static I? Then(this Result result, Func Success) { return result.IsSuccess ? Success() : default; } /// /// Executes a function based on the success or failure of the task result, /// using the data in the result. /// /// The type of the data in the result. /// The type of the return value. /// The task returning a data result to evaluate. /// The function to execute if the data result is successful. /// The function to execute if the data result is a failure. /// The result of the executed function. public static async Task ThenAsync(this Result result, Func> SuccessAsync) { return result.IsSuccess ? await SuccessAsync() : default; } /// /// Executes a function based on the success or failure of the result. /// /// The type of the return value. /// The result to evaluate. /// The function to execute if the result is successful. /// The function to execute if the result is a failure. /// The result of the executed function. public static I Then(this Result result, Func Success, Func, List, I> Fail) { return result.IsSuccess ? Success() : Fail(result.Messages, result.Notices); } /// /// Asynchronously executes a function based on the success or failure of the result. /// /// The type of the return value. /// The result to evaluate. /// The asynchronous function to execute if the result is successful. /// The function to execute if the result is a failure. /// The result of the executed function. public static async Task ThenAsync(this Result result, Func> SuccessAsync, Func, List, I> Fail) { return result.IsSuccess ? await SuccessAsync() : Fail(result.Messages, result.Notices); } /// /// Executes a function based on the success or failure of the data result. /// /// The type of the data in the result. /// The type of the return value. /// The data result to evaluate. /// The function to execute if the data result is successful. /// The function to execute if the data result is a failure. /// The result of the executed function. public static I Then(this DataResult result, Func Success, Func, List, I> Fail) { return result.IsSuccess ? Success(result.Data) : Fail(result.Messages, result.Notices); } /// /// Asynchronously executes a function based on the success or failure of the data result. /// /// The type of the data in the result. /// The type of the return value. /// The data result to evaluate. /// The asynchronous function to execute if the data result is successful. /// The function to execute if the data result is a failure. /// The result of the executed function. public static async Task ThenAsync(this DataResult result, Func> SuccessAsync, Func, List, I> Fail) { return result.IsSuccess ? await SuccessAsync(result.Data) : Fail(result.Messages, result.Notices); } /// /// Asynchronously executes a function based on the success or failure of a task returning a result. /// /// The type of the return value. /// The task returning a result to evaluate. /// The function to execute if the result is successful. /// The function to execute if the result is a failure. /// The result of the executed function. public static async Task ThenAsync(this Task tResult, Func Success, Func, List, I> Fail) { Result result = await tResult; return result.IsSuccess ? Success() : Fail(result.Messages, result.Notices); } /// /// Asynchronously executes a function based on the success or failure of a task returning a result. /// /// The type of the return value. /// The task returning a result to evaluate. /// The asynchronous function to execute if the result is successful. /// The function to execute if the result is a failure. /// The result of the executed function. public static async Task ThenAsync(this Task tResult, Func> SuccessAsync, Func, List, I> Fail) { Result result = await tResult; return result.IsSuccess ? await SuccessAsync() : Fail(result.Messages, result.Notices); } /// /// Asynchronously executes a function based on the success or failure of a task returning a data result. /// /// The type of the data in the result. /// The type of the return value. /// The task returning a data result to evaluate. /// The function to execute if the data result is successful. /// The function to execute if the data result is a failure. /// The result of the executed function. public static async Task ThenAsync(this Task> tResult, Func Success, Func, List, I> Fail) { DataResult result = await tResult; return result.IsSuccess ? Success(result.Data) : Fail(result.Messages, result.Notices); } /// /// Asynchronously executes a function based on the success or failure of a task returning a data result. /// /// The type of the data in the result. /// The type of the return value. /// The task returning a data result to evaluate. /// The asynchronous function to execute if the data result is successful. /// The function to execute if the data result is a failure. /// The result of the executed function. public static async Task ThenAsync(this Task> tResult, Func> SuccessAsync, Func, List, I> Fail) { DataResult result = await tResult; return result.IsSuccess ? await SuccessAsync(result.Data) : Fail(result.Messages, result.Notices); } /// /// Joins the values into a single string with optional start, separator, and end strings. /// /// The type of the values. /// The values to join. /// The starting string. /// The separator string. /// The ending string. /// The joined string. public static string Join(this IEnumerable values, string start = "", string seperator = ". ", string end = ".") => new StringBuilder(start).Append(string.Join(seperator, values)).Append(end).ToString(); /// /// Logs the notices using the specified logger. /// /// The logger to use. /// The collection of notices to log. /// The starting string for each notice. /// The separator string for messages in each notice. /// The ending string for each notice. public static void LogNotice(this ILogger logger, IEnumerable notices, string start = ": ", string seperator = ". ", string end = ".\n") { foreach(LogLevel level in Enum.GetValues(typeof(LogLevel))) { var logNotices = notices.Where(n => n.Level == level); if (!logNotices.Any()) continue; var sb = new StringBuilder(); foreach(Notice notice in logNotices) { if (notice.Flag is not null) sb.Append(notice.Flag); if (notice.Messages.Any()) sb.Append(start).Append(string.Join(seperator, notice.Messages)).AppendLine(end); else sb.Append(end); } logger.Log(level, sb.ToString()); } } /// /// Logs the notices from a result using the specified logger. /// /// The logger to use. /// The result containing the notices to log. /// The starting string for each notice. /// The separator string for messages in each notice. /// The ending string for each notice. public static void LogNotice(this ILogger logger, Result result, string start = ": ", string seperator = ". ", string end = ".\n") => logger.LogNotice(notices: result.Notices, start: start, seperator: seperator, end: end); /// /// Determines if the data result is right (true). /// /// The data result to evaluate. /// True if the data result is true; otherwise, false. public static bool IsRight(this DataResult bResult) => bResult.Data; /// /// Determines if the data result is wrong (false). /// /// The data result to evaluate. /// True if the data result is false; otherwise, false. public static bool IsWrong(this DataResult bResult) => !bResult.Data; } }