From 26a1c604d5776100be08a70dd4a4393d85f39016 Mon Sep 17 00:00:00 2001 From: JustFixMe Date: Fri, 8 Dec 2023 17:54:17 +0400 Subject: [PATCH] added Try extensions generator --- ...erator.cs => ExtensionsMethodGenerator.cs} | 3 +- .../TryExtensionsExecutor.cs | 211 ++++++++++++++++++ Railway/Try.cs | 105 +-------- 3 files changed, 214 insertions(+), 105 deletions(-) rename Railway.SourceGenerator/{ResultMethodGenerator.cs => ExtensionsMethodGenerator.cs} (88%) create mode 100644 Railway.SourceGenerator/TryExtensionsExecutor.cs diff --git a/Railway.SourceGenerator/ResultMethodGenerator.cs b/Railway.SourceGenerator/ExtensionsMethodGenerator.cs similarity index 88% rename from Railway.SourceGenerator/ResultMethodGenerator.cs rename to Railway.SourceGenerator/ExtensionsMethodGenerator.cs index 8b62141..097f13b 100644 --- a/Railway.SourceGenerator/ResultMethodGenerator.cs +++ b/Railway.SourceGenerator/ExtensionsMethodGenerator.cs @@ -8,7 +8,7 @@ using Microsoft.CodeAnalysis; namespace Just.Railway.SourceGen; [Generator] -public class ResultMethodGenerator : IIncrementalGenerator +public class ExtensionsMethodGenerator : IIncrementalGenerator { private readonly IEnumerable _executors = new IGeneratorExecutor[] { @@ -18,6 +18,7 @@ public class ResultMethodGenerator : IIncrementalGenerator new ResultBindExecutor(), new ResultTapExecutor(), new ResultAppendExecutor(), + new TryExtensionsExecutor(), }; public void Initialize(IncrementalGeneratorInitializationContext context) diff --git a/Railway.SourceGenerator/TryExtensionsExecutor.cs b/Railway.SourceGenerator/TryExtensionsExecutor.cs new file mode 100644 index 0000000..30ff82e --- /dev/null +++ b/Railway.SourceGenerator/TryExtensionsExecutor.cs @@ -0,0 +1,211 @@ +using System; +using System.Collections.Immutable; +using System.Linq; +using System.Text; +using Microsoft.CodeAnalysis; + +namespace Just.Railway.SourceGen; + +public sealed class TryExtensionsExecutor : IGeneratorExecutor +{ + public void Execute(SourceProductionContext context, Compilation source) + { + var methods = GenerateMethods(); + var code = $$""" + #nullable enable + using System; + using System.Collections.Generic; + using System.Diagnostics.Contracts; + using System.CodeDom.Compiler; + + namespace Just.Railway; + + public static partial class Try + { + {{methods}} + } + """; + + context.AddSource("Try.Run.g.cs", code); + } + + private string GenerateMethods() + { + var sb = new StringBuilder(); + + for (int i = 0; i <= Constants.MaxResultTupleSize; i++) + { + GenerateMethodsForArgCount(sb, argCount: i); + } + + return sb.ToString(); + } + + private void GenerateMethodsForArgCount(StringBuilder sb, int argCount) + { + var templateArgNames = Enumerable.Range(1, argCount) + .Select(i => $"T{i}") + .ToImmutableArray(); + var argNames = Enumerable.Range(1, argCount) + .Select(i => $"arg{i}") + .ToImmutableArray(); + + string actionTemplateDecl = GenerateTemplateDecl(templateArgNames); + string resultActionTemplateDecl = GenerateTemplateDecl(templateArgNames.Add("Result")); + string funcTemplateDecl = GenerateTemplateDecl(templateArgNames.Add("TResult")); + string resultFuncTemplateDecl = GenerateTemplateDecl(templateArgNames.Add("Result")); + string argumentsDeclExpansion = string.Join(", ", templateArgNames.Zip(argNames, (t, n) => $"{t} {n}")); + string argumentsExpansion = string.Join(", ", argNames); + + sb.AppendLine($"#region <{string.Join(", ", templateArgNames)}>"); + + sb.AppendLine($$""" + [PureAttribute] + [GeneratedCodeAttribute("{{nameof(TryExtensionsExecutor)}}", "1.0.0.0")] + public static Result Run{{actionTemplateDecl}}(Action{{actionTemplateDecl}} action{{TrailingArguments(argumentsDeclExpansion)}}) + { + try + { + action({{argumentsExpansion}}); + return Result.Success(); + } + catch (Exception ex) + { + return Error.New(ex); + } + } + """); + + sb.AppendLine($$""" + [PureAttribute] + [GeneratedCodeAttribute("{{nameof(TryExtensionsExecutor)}}", "1.0.0.0")] + public static Result Run{{actionTemplateDecl}}(Func{{resultActionTemplateDecl}} action{{TrailingArguments(argumentsDeclExpansion)}}) + { + try + { + return action({{argumentsExpansion}}); + } + catch (Exception ex) + { + return Error.New(ex); + } + } + """); + + sb.AppendLine($$""" + [PureAttribute] + [GeneratedCodeAttribute("{{nameof(TryExtensionsExecutor)}}", "1.0.0.0")] + public static Result Run{{funcTemplateDecl}}(Func{{funcTemplateDecl}} func{{TrailingArguments(argumentsDeclExpansion)}}) + { + try + { + return Result.Success(func({{argumentsExpansion}})); + } + catch (Exception ex) + { + return Error.New(ex); + } + } + """); + + sb.AppendLine($$""" + [PureAttribute] + [GeneratedCodeAttribute("{{nameof(TryExtensionsExecutor)}}", "1.0.0.0")] + public static Result Run{{funcTemplateDecl}}(Func{{resultFuncTemplateDecl}} func{{TrailingArguments(argumentsDeclExpansion)}}) + { + try + { + return func({{argumentsExpansion}}); + } + catch (Exception ex) + { + return Error.New(ex); + } + } + """); + + GenerateAsyncMethods(sb, templateArgNames, actionTemplateDecl, funcTemplateDecl, argumentsDeclExpansion, argumentsExpansion, "Task"); + GenerateAsyncMethods(sb, templateArgNames, actionTemplateDecl, funcTemplateDecl, argumentsDeclExpansion, argumentsExpansion, "ValueTask"); + + sb.AppendLine("#endregion"); + } + + private static void GenerateAsyncMethods(StringBuilder sb, ImmutableArray templateArgNames, string actionTemplateDecl, string funcTemplateDecl, string argumentsDeclExpansion, string argumentsExpansion, string taskType) + { + string actionTaskTemplateDecl = GenerateTemplateDecl(templateArgNames.Add(taskType)); + string resultActionTaskTemplateDecl = GenerateTemplateDecl(templateArgNames.Add($"{taskType}")); + string funcTaskTemplateDecl = GenerateTemplateDecl(templateArgNames.Add($"{taskType}")); + string resultFuncTaskTemplateDecl = GenerateTemplateDecl(templateArgNames.Add($"{taskType}>")); + sb.AppendLine($$""" + [PureAttribute] + [GeneratedCodeAttribute("{{nameof(TryExtensionsExecutor)}}", "1.0.0.0")] + public static async {{taskType}} Run{{actionTemplateDecl}}(Func{{actionTaskTemplateDecl}} action{{TrailingArguments(argumentsDeclExpansion)}}) + { + try + { + await action({{argumentsExpansion}}).ConfigureAwait(false); + return Result.Success(); + } + catch (Exception ex) + { + return Error.New(ex); + } + } + """); + + sb.AppendLine($$""" + [PureAttribute] + [GeneratedCodeAttribute("{{nameof(TryExtensionsExecutor)}}", "1.0.0.0")] + public static async {{taskType}} Run{{actionTemplateDecl}}(Func{{resultActionTaskTemplateDecl}} action{{TrailingArguments(argumentsDeclExpansion)}}) + { + try + { + return await action({{argumentsExpansion}}).ConfigureAwait(false); + } + catch (Exception ex) + { + return Error.New(ex); + } + } + """); + + sb.AppendLine($$""" + [PureAttribute] + [GeneratedCodeAttribute("{{nameof(TryExtensionsExecutor)}}", "1.0.0.0")] + public static async {{taskType}}> Run{{funcTemplateDecl}}(Func{{funcTaskTemplateDecl}} func{{TrailingArguments(argumentsDeclExpansion)}}) + { + try + { + return Result.Success(await func({{argumentsExpansion}}).ConfigureAwait(false)); + } + catch (Exception ex) + { + return Error.New(ex); + } + } + """); + + sb.AppendLine($$""" + [PureAttribute] + [GeneratedCodeAttribute("{{nameof(TryExtensionsExecutor)}}", "1.0.0.0")] + public static async {{taskType}}> Run{{funcTemplateDecl}}(Func{{resultFuncTaskTemplateDecl}} func{{TrailingArguments(argumentsDeclExpansion)}}) + { + try + { + return await func({{argumentsExpansion}}).ConfigureAwait(false); + } + catch (Exception ex) + { + return Error.New(ex); + } + } + """); + } + + private static string TrailingArguments(string argumentsExpansion) => string.IsNullOrEmpty(argumentsExpansion) + ? string.Empty + : $", {argumentsExpansion}"; + private static string GenerateTemplateDecl(ImmutableArray templateArgNames) => templateArgNames.Length > 0 + ? $"<{string.Join(", ", templateArgNames)}>" + : string.Empty; +} diff --git a/Railway/Try.cs b/Railway/Try.cs index 1d598c6..f8200e3 100644 --- a/Railway/Try.cs +++ b/Railway/Try.cs @@ -1,108 +1,5 @@ namespace Just.Railway; -public static class Try +public static partial class Try { - public static Result Run(Action action) - { - try - { - action(); - return Result.Success(); - } - catch (Exception ex) - { - return Result.Failure(ex); - } - } - public static async Task Run(Func action) - { - try - { - await action().ConfigureAwait(false); - return Result.Success(); - } - catch (Exception ex) - { - return Result.Failure(ex); - } - } - public static async ValueTask Run(Func action) - { - try - { - await action().ConfigureAwait(false); - return Result.Success(); - } - catch (Exception ex) - { - return Result.Failure(ex); - } - } - public static Result Run(Func func) - { - try - { - return Result.Success(func()); - } - catch (Exception ex) - { - return Result.Failure(ex); - } - } - public static async Task> Run(Func> func) - { - try - { - return Result.Success(await func().ConfigureAwait(false)); - } - catch (Exception ex) - { - return Result.Failure(ex); - } - } - public static async ValueTask> Run(Func> func) - { - try - { - return Result.Success(await func().ConfigureAwait(false)); - } - catch (Exception ex) - { - return Result.Failure(ex); - } - } - - public static Result Run(Func> func) - { - try - { - return func(); - } - catch (Exception ex) - { - return Result.Failure(ex); - } - } - public static async Task> Run(Func>> func) - { - try - { - return await func().ConfigureAwait(false); - } - catch (Exception ex) - { - return Result.Failure(ex); - } - } - public static async ValueTask> Run(Func>> func) - { - try - { - return await func().ConfigureAwait(false); - } - catch (Exception ex) - { - return Result.Failure(ex); - } - } }