private static string GenerateParameterSource( INamedTypeSymbol @class, IMethodSymbol method, IParameterSymbol parameter) { var namespaces = string.Join("\n", AlwaysActiveNamespaces .Append(@class.ContainingNamespace.ToDisplayString()) .Append(@parameter.Type.ContainingNamespace.ToDisplayString()) .Distinct() .OrderBy(x => x) .Select(x => $"using {x};")); var reflectionTypes = string.Join(", ", method.Parameters .Select(x => $"typeof({x.ToDisplayString()})")); return ($@"{namespaces} namespace Finite.Commands.AttributedModel.Internal.Commands {{ internal class CommandFactory__{@class.Name}__{method.Name}__{parameter.Name} : IParameter {{ private static readonly ParameterInfo Parameter = typeof({@class.Name}) .GetMethod( name: ""{method.Name}"", genericParameterCount: {method.Arity}, bindingAttr: {GetBindingFlags(method)}, binder: default, callConvention: {GetCallConv(method)}, types: new[] {{ {reflectionTypes} }}, modifiers: null)! .GetParameters()[{parameter.Ordinal}]!; private IReadOnlyDictionary<object, object?>? _data; public string Name {{ get; }} = ""{parameter.Name}""; public Type Type {{ get; }} = typeof({parameter.Type.ToDisplayString()}); public IReadOnlyDictionary<object, object?> Data {{ get {{ return _data ??= new Dictionary<object, object?>( GetData()); }} }} private static IEnumerable<KeyValuePair<object, object?>> GetData() => DataProvider.GetData(Parameter); }} }}"); }
private static string GenerateDataProviderSource( IEnumerable <string> providers) { var namespaces = string.Join("\n", AlwaysActiveNamespaces .Distinct() .OrderBy(x => x) .Select(x => $"using {x};")); var providerFactoryTypes = string.Join(", ", providers.Select(x => $"new {x}()")); return ($@"{namespaces} namespace Finite.Commands.AttributedModel.Internal.Commands {{ internal static class DataProvider {{ private static readonly IAdditionalDataProviderFactory[] Factories = new IAdditionalDataProviderFactory[] {{ {providerFactoryTypes} }}; public static IEnumerable<KeyValuePair<object, object?>> GetData( MethodInfo method) {{ foreach (var factory in Factories) foreach (var provider in factory.GetDataProvider(method)) foreach (var kvp in provider.GetData()) yield return kvp; }} public static IEnumerable<KeyValuePair<object, object?>> GetData( ParameterInfo parameter) {{ foreach (var factory in Factories) foreach (var provider in factory.GetDataProvider(parameter)) foreach (var kvp in provider.GetData()) yield return kvp; }} }} }}"); }
private static string GenerateCommandSource( INamedTypeSymbol @class, IMethodSymbol method, INamedTypeSymbol groupAttributeSymbol, INamedTypeSymbol commandAttributeSymbol) { string?commandPath; { var segment = GetStringFromAttribute(method, commandAttributeSymbol); commandPath = $"new CommandString(\"{segment}\")"; var currentClass = @class; do { segment = GetStringFromAttribute(currentClass, groupAttributeSymbol); commandPath = "CommandPath.Combine(" + $"new CommandString(\"{segment}\"), " + $"{commandPath})"; currentClass = @class.ContainingType; }while (currentClass != null); } var parameterNamespaces = method.Parameters .Select(x => x.Type.ContainingNamespace.ToDisplayString()); var parameterTypes = string.Join(", ", method.Parameters .Select( x => $"new CommandFactory__{@class.Name}__{method.Name}__{x.Name}()")); var parameterAccessors = string.Join(", ", method.Parameters .Select( x => $"({x.Type.ToDisplayString()})(context.Parameters[\"{x.Name}\"]!)")); var namespaces = string.Join("\n", AlwaysActiveNamespaces .Concat(parameterNamespaces) .Append(@class.ContainingNamespace.ToDisplayString()) .Distinct() .OrderBy(x => x) .Select(x => $"using {x};")); var reflectionTypes = string.Join(", ", method.Parameters .Select(x => $"typeof({x.ToDisplayString()})")); return ($@"{namespaces} namespace Finite.Commands.AttributedModel.Internal.Commands {{ internal class CommandFactory__{@class.Name}__{method.Name} : ICommand {{ private static readonly ObjectFactory CommandClassFactory = ActivatorUtilities.CreateFactory( typeof({@class.Name}), Array.Empty<Type>()); private static readonly MethodInfo Method = typeof({@class.Name}) .GetMethod( name: ""{method.Name}"", genericParameterCount: {method.Arity}, bindingAttr: {GetBindingFlags(method)}, binder: default, callConvention: {GetCallConv(method)}, types: new[] {{ {reflectionTypes} }}, modifiers: null)!; private IReadOnlyDictionary<object, object?>? _data; public CommandString Name {{ get; }} = {commandPath}; public IReadOnlyList<IParameter> Parameters {{ get; }} = new IParameter[] {{ {parameterTypes} }}; public IReadOnlyDictionary<object, object?> Data {{ get {{ return _data ??= new Dictionary<object, object?>( GetData()); }} }} public async ValueTask<ICommandResult> ExecuteAsync( CommandContext context, CancellationToken cancellationToken) {{ var commandClass = ({@class.Name})CommandClassFactory( context.Services, Array.Empty<object?>()); Module.SetCommandContext(commandClass, context); Module.SetCancellationToken(commandClass, cancellationToken); try {{ return await commandClass.{method.Name}({parameterAccessors}); }} finally {{ if (commandClass is IDisposable disposable) disposable.Dispose(); }} }} private static IEnumerable<KeyValuePair<object, object?>> GetData() => DataProvider.GetData(Method); }} }}"); }