diff --git a/Railway.SourceGenerator/ResultMethodGenerator.cs b/Railway.SourceGenerator/ResultMethodGenerator.cs index 8e917c6..913f1e4 100644 --- a/Railway.SourceGenerator/ResultMethodGenerator.cs +++ b/Railway.SourceGenerator/ResultMethodGenerator.cs @@ -16,6 +16,7 @@ public class ResultMethodGenerator : IIncrementalGenerator new ResultMatchExecutor(), new ResultMapExecutor(), new ResultBindExecutor(), + new ResultTapExecutor(), }; public void Initialize(IncrementalGeneratorInitializationContext context) diff --git a/Railway.SourceGenerator/ResultTapExecutor.cs b/Railway.SourceGenerator/ResultTapExecutor.cs new file mode 100644 index 0000000..954dc2f --- /dev/null +++ b/Railway.SourceGenerator/ResultTapExecutor.cs @@ -0,0 +1,112 @@ +using System.Collections.Immutable; +using System.Linq; +using System.Text; +using Microsoft.CodeAnalysis; + +namespace Just.Railway.SourceGen; + +internal sealed class ResultTapExecutor : ResultExtensionsExecutor +{ + protected override string ExtensionType => "Tap"; + + protected override void GenerateMethodsForArgCount(StringBuilder sb, int argCount) + { + var templateArgNames = Enumerable.Range(1, argCount) + .Select(i => $"T{i}") + .ToImmutableArray(); + string separatedTemplateArgs = string.Join(", ", templateArgNames); + + sb.AppendLine($"#region <{separatedTemplateArgs}>"); + + string resultValueType = templateArgNames.Length == 1 ? separatedTemplateArgs : $"({separatedTemplateArgs})"; + string resultValueExpansion = GenerateResultValueExpansion(templateArgNames); + + sb.AppendLine($$""" + [PureAttribute] + [GeneratedCodeAttribute("{{nameof(ResultTapExecutor)}}", "1.0.0.0")] + public static ref readonly Result<{{resultValueType}}> Tap<{{separatedTemplateArgs}}>(this in Result<{{resultValueType}}> result, Action<{{separatedTemplateArgs}}>? onSuccess = null, Action? onFailure = null) + { + switch (result.State) + { + case ResultState.Success: + onSuccess?.Invoke({{resultValueExpansion}}); + break; + case ResultState.Error: + onFailure?.Invoke(result.Error!); + break; + + default: throw new ResultNotInitializedException(nameof(result)); + } + return ref result; + } + """); + + sb.AppendLine($$""" + [PureAttribute] + [GeneratedCodeAttribute("{{nameof(ResultTapExecutor)}}", "1.0.0.0")] + public static async Task> Tap<{{separatedTemplateArgs}}>(this Task> resultTask, Action<{{separatedTemplateArgs}}>? onSuccess = null, Action? onFailure = null) + { + var result = await resultTask.ConfigureAwait(false); + switch (result.State) + { + case ResultState.Success: + onSuccess?.Invoke({{resultValueExpansion}}); + break; + case ResultState.Error: + onFailure?.Invoke(result.Error!); + break; + + default: throw new ResultNotInitializedException(nameof(resultTask)); + } + return result; + } + """); + + sb.AppendLine($$""" + [PureAttribute] + [GeneratedCodeAttribute("{{nameof(ResultTapExecutor)}}", "1.0.0.0")] + public static async Task> Tap<{{separatedTemplateArgs}}>(this Result<{{resultValueType}}> result, Func<{{separatedTemplateArgs}}, Task>? onSuccess = null, Func? onFailure = null) + { + switch (result.State) + { + case ResultState.Success: + if (onSuccess is not null) + await onSuccess.Invoke({{resultValueExpansion}}).ConfigureAwait(false); + break; + case ResultState.Error: + if (onFailure is not null) + await onFailure.Invoke(result.Error!).ConfigureAwait(false); + break; + + default: throw new ResultNotInitializedException(nameof(result)); + } + return result; + } + """); + + sb.AppendLine($$""" + [PureAttribute] + [GeneratedCodeAttribute("{{nameof(ResultTapExecutor)}}", "1.0.0.0")] + public static async Task> Tap<{{separatedTemplateArgs}}>(this Task> resultTask, Func<{{separatedTemplateArgs}}, Task>? onSuccess = null, Func? onFailure = null) + { + var result = await resultTask.ConfigureAwait(false); + switch (result.State) + { + case ResultState.Success: + if (onSuccess is not null) + await onSuccess.Invoke({{resultValueExpansion}}).ConfigureAwait(false); + break; + case ResultState.Error: + if (onFailure is not null) + await onFailure.Invoke(result.Error!).ConfigureAwait(false); + break; + + default: throw new ResultNotInitializedException(nameof(resultTask)); + } + return result; + } + """); + + sb.AppendLine("#endregion"); + } +} diff --git a/Railway/ResultExtensions.cs b/Railway/ResultExtensions.cs index 19d4230..a7941da 100644 --- a/Railway/ResultExtensions.cs +++ b/Railway/ResultExtensions.cs @@ -686,7 +686,7 @@ public static partial class ResultExtensions #endregion - #region Tap + #region Tap<> [Pure] public static ref readonly Result Tap(this in Result result, Action? onSuccess = null, Action? onFailure = null) { @@ -762,289 +762,6 @@ public static partial class ResultExtensions return result; } - [Pure] - public static ref readonly Result Tap(this in Result result, Action? onSuccess = null, Action? onFailure = null) - { - switch (result.State) - { - case ResultState.Success: - onSuccess?.Invoke(result.Value!); - break; - case ResultState.Error: - onFailure?.Invoke(result.Error!); - break; - - default: throw new ResultNotInitializedException(nameof(result)); - } - return ref result; - } - [Pure] - public static async Task> Tap(this Task> resultTask, Action? onSuccess = null, Action? onFailure = null) - { - var result = await resultTask.ConfigureAwait(false); - switch (result.State) - { - case ResultState.Success: - onSuccess?.Invoke(result.Value!); - break; - case ResultState.Error: - onFailure?.Invoke(result.Error!); - break; - - default: throw new ResultNotInitializedException(nameof(resultTask)); - } - return result; - } - [Pure] - public static async Task> Tap(this Result result, Func? onSuccess = null, Func? onFailure = null) - { - switch (result.State) - { - case ResultState.Success: - if (onSuccess is not null) - await onSuccess.Invoke(result.Value!).ConfigureAwait(false); - break; - case ResultState.Error: - if (onFailure is not null) - await onFailure.Invoke(result.Error!).ConfigureAwait(false); - break; - - default: throw new ResultNotInitializedException(nameof(result)); - } - return result; - } - [Pure] - public static async Task> Tap(this Task> resultTask, Func? onSuccess = null, Func? onFailure = null) - { - var result = await resultTask.ConfigureAwait(false); - switch (result.State) - { - case ResultState.Success: - if (onSuccess is not null) - await onSuccess.Invoke(result.Value!).ConfigureAwait(false); - break; - case ResultState.Error: - if (onFailure is not null) - await onFailure.Invoke(result.Error!).ConfigureAwait(false); - break; - - default: throw new ResultNotInitializedException(nameof(resultTask)); - } - return result; - } - - [Pure] - public static ref readonly Result<(T1, T2)> Tap(this in Result<(T1, T2)> result, Action? onSuccess = null, Action? onFailure = null) - { - switch (result.State) - { - case ResultState.Success: - onSuccess?.Invoke(result.Value.Item1, result.Value.Item2); - break; - case ResultState.Error: - onFailure?.Invoke(result.Error!); - break; - - default: throw new ResultNotInitializedException(nameof(result)); - } - return ref result; - } - [Pure] - public static async Task> Tap(this Task> resultTask, Action? onSuccess = null, Action? onFailure = null) - { - var result = await resultTask.ConfigureAwait(false); - switch (result.State) - { - case ResultState.Success: - onSuccess?.Invoke(result.Value.Item1, result.Value.Item2); - break; - case ResultState.Error: - onFailure?.Invoke(result.Error!); - break; - - default: throw new ResultNotInitializedException(nameof(resultTask)); - } - return result; - } - [Pure] - public static async Task> Tap(this Result<(T1, T2)> result, Func? onSuccess = null, Func? onFailure = null) - { - switch (result.State) - { - case ResultState.Success: - if (onSuccess is not null) - await onSuccess.Invoke(result.Value.Item1, result.Value.Item2).ConfigureAwait(false); - break; - case ResultState.Error: - if (onFailure is not null) - await onFailure.Invoke(result.Error!).ConfigureAwait(false); - break; - - default: throw new ResultNotInitializedException(nameof(result)); - } - return result; - } - [Pure] - public static async Task> Tap(this Task> resultTask, Func? onSuccess = null, Func? onFailure = null) - { - var result = await resultTask.ConfigureAwait(false); - switch (result.State) - { - case ResultState.Success: - if (onSuccess is not null) - await onSuccess.Invoke(result.Value.Item1, result.Value.Item2).ConfigureAwait(false); - break; - case ResultState.Error: - if (onFailure is not null) - await onFailure.Invoke(result.Error!).ConfigureAwait(false); - break; - - default: throw new ResultNotInitializedException(nameof(resultTask)); - } - return result; - } - - [Pure] - public static ref readonly Result<(T1, T2, T3)> Tap(this in Result<(T1, T2, T3)> result, Action? onSuccess = null, Action? onFailure = null) - { - switch (result.State) - { - case ResultState.Success: - onSuccess?.Invoke(result.Value.Item1, result.Value.Item2, result.Value.Item3); - break; - case ResultState.Error: - onFailure?.Invoke(result.Error!); - break; - - default: throw new ResultNotInitializedException(nameof(result)); - } - return ref result; - } - [Pure] - public static async Task> Tap(this Task> resultTask, Action? onSuccess = null, Action? onFailure = null) - { - var result = await resultTask.ConfigureAwait(false); - switch (result.State) - { - case ResultState.Success: - onSuccess?.Invoke(result.Value.Item1, result.Value.Item2, result.Value.Item3); - break; - case ResultState.Error: - onFailure?.Invoke(result.Error!); - break; - - default: throw new ResultNotInitializedException(nameof(resultTask)); - } - return result; - } - [Pure] - public static async Task> Tap(this Result<(T1, T2, T3)> result, Func? onSuccess = null, Func? onFailure = null) - { - switch (result.State) - { - case ResultState.Success: - if (onSuccess is not null) - await onSuccess.Invoke(result.Value.Item1, result.Value.Item2, result.Value.Item3).ConfigureAwait(false); - break; - case ResultState.Error: - if (onFailure is not null) - await onFailure.Invoke(result.Error!).ConfigureAwait(false); - break; - - default: throw new ResultNotInitializedException(nameof(result)); - } - return result; - } - [Pure] - public static async Task> Tap(this Task> resultTask, Func? onSuccess = null, Func? onFailure = null) - { - var result = await resultTask.ConfigureAwait(false); - switch (result.State) - { - case ResultState.Success: - if (onSuccess is not null) - await onSuccess.Invoke(result.Value.Item1, result.Value.Item2, result.Value.Item3).ConfigureAwait(false); - break; - case ResultState.Error: - if (onFailure is not null) - await onFailure.Invoke(result.Error!).ConfigureAwait(false); - break; - - default: throw new ResultNotInitializedException(nameof(resultTask)); - } - return result; - } - - [Pure] - public static ref readonly Result<(T1, T2, T3, T4)> Tap(this in Result<(T1, T2, T3, T4)> result, Action? onSuccess = null, Action? onFailure = null) - { - switch (result.State) - { - case ResultState.Success: - onSuccess?.Invoke(result.Value.Item1, result.Value.Item2, result.Value.Item3, result.Value.Item4); - break; - case ResultState.Error: - onFailure?.Invoke(result.Error!); - break; - - default: throw new ResultNotInitializedException(nameof(result)); - } - return ref result; - } - [Pure] - public static async Task> Tap(this Task> resultTask, Action? onSuccess = null, Action? onFailure = null) - { - var result = await resultTask.ConfigureAwait(false); - switch (result.State) - { - case ResultState.Success: - onSuccess?.Invoke(result.Value.Item1, result.Value.Item2, result.Value.Item3, result.Value.Item4); - break; - case ResultState.Error: - onFailure?.Invoke(result.Error!); - break; - - default: throw new ResultNotInitializedException(nameof(resultTask)); - } - return result; - } - [Pure] - public static async Task> Tap(this Result<(T1, T2, T3, T4)> result, Func? onSuccess = null, Func? onFailure = null) - { - switch (result.State) - { - case ResultState.Success: - if (onSuccess is not null) - await onSuccess.Invoke(result.Value.Item1, result.Value.Item2, result.Value.Item3, result.Value.Item4).ConfigureAwait(false); - break; - case ResultState.Error: - if (onFailure is not null) - await onFailure.Invoke(result.Error!).ConfigureAwait(false); - break; - - default: throw new ResultNotInitializedException(nameof(result)); - } - return result; - } - [Pure] - public static async Task> Tap(this Task> resultTask, Func? onSuccess = null, Func? onFailure = null) - { - var result = await resultTask.ConfigureAwait(false); - switch (result.State) - { - case ResultState.Success: - if (onSuccess is not null) - await onSuccess.Invoke(result.Value.Item1, result.Value.Item2, result.Value.Item3, result.Value.Item4).ConfigureAwait(false); - break; - case ResultState.Error: - if (onFailure is not null) - await onFailure.Invoke(result.Error!).ConfigureAwait(false); - break; - - default: throw new ResultNotInitializedException(nameof(resultTask)); - } - return result; - } #endregion #region Merge