public static ReplBuilder FromInstance(IServiceProvider serviceProvider, IReplBase replInstance) { if (replInstance is null) { throw new ArgumentNullException(nameof(replInstance)); } var replType = replInstance.GetType(); var builder = new ReplBuilder(); var controller = replType.GetCustomAttribute <ReplControllerAttribute>(); if (controller != null) { builder.WithCaption(controller.Caption) .WithVersion(controller.Version) .WithDescription(controller.Description); } builder.WithPrompt(() => replInstance.Prompt); if (replInstance.CanPersistState) { builder.WithStatePersistence( () => replInstance.PersistState(), state => replInstance.LoadState(state)); } foreach (var replExample in replType.GetCustomAttributes <ReplExampleAttribute>()) { builder.WithExample(replExample.Command, exampleBuilder => { exampleBuilder .WithCaption(exampleBuilder.caption) .WithDescription(exampleBuilder.description); }); } foreach (var method in replType.GetMethods(BindingFlags.Public | BindingFlags.Instance)) { var command = method.GetCustomAttribute <ReplCommandAttribute>(); if (command != null) { builder.WithCommand(command.Name, commandBuilder => BuildCommand(serviceProvider, replInstance, builder, method, command, commandBuilder)); } } return(builder); }
private static void BuildCommand( IServiceProvider serviceProvider, IReplBase replInstance, ReplBuilder builder, MethodInfo method, ReplCommandAttribute command, ReplCommandBuilder commandBuilder) { commandBuilder .WithCaption(command.Caption) .WithDescription(command.Description); foreach (var namesAttr in method.GetCustomAttributes <ReplNamesAttribute>()) { foreach (var name in namesAttr.Names) { commandBuilder.WithName(name); } } foreach (var replExample in method.GetCustomAttributes <ReplExampleAttribute>()) { commandBuilder.WithExample(replExample.Command, exampleBuilder => { exampleBuilder .WithCaption(exampleBuilder.caption) .WithDescription(exampleBuilder.description); }); if (replExample.Scope >= ReplExampleScope.Parent) { builder.WithExample(replExample.Command, exampleBuilder => { exampleBuilder .WithCaption(exampleBuilder.caption) .WithDescription(exampleBuilder.description); }); } } var parameters = method.GetParameters(); foreach (var parameter in parameters) { var replParam = parameter.GetCustomAttribute <ReplParamAttribute>(); var replOption = parameter.GetCustomAttribute <ReplOptionAttribute>(); var replFlag = parameter.GetCustomAttribute <ReplFlagAttribute>(); var fromServiceProvider = parameter.GetCustomAttribute <ReplServiceAttribute>() != null; if (replParam != null) { commandBuilder.WithPositional(parameter.Name, paramBuilder => { BuildPositionalParameter(builder, commandBuilder, parameter, replParam, paramBuilder); }); } else if (replOption != null) { commandBuilder.WithOption(parameter.Name, paramBuilder => { BuildOptionParameter(builder, commandBuilder, parameter, replOption, paramBuilder); }); } else if (replFlag != null) { commandBuilder.WithFlag(parameter.Name, paramBuilder => { BuildFlagParameter(builder, commandBuilder, parameter, replFlag, paramBuilder); }); } else { if (!parameter.HasDefaultValue && !fromServiceProvider && parameter.ParameterType != typeof(CancellationToken) && parameter.ParameterType != typeof(Dictionary <string, object>)) { string message = $"Parameter {parameter.Name} from command {method.Name} cannot be fulfilled"; ConsoleEx.WriteLine(ConsoleColor.Red, message); throw new InvalidOperationException(message); } } } Task ExecuteAsync(Dictionary <string, object> values, CancellationToken cancellationToken) { var paramValues = new object[parameters.Length]; // TODO: Optimize for (int i = 0; i < parameters.Length; i++) { if (values.TryGetValue(parameters[i].Name, out var value)) { // TODO: Bind depending on the param type paramValues[i] = value; } else if (parameters[i].GetCustomAttribute <ReplServiceAttribute>() != null) { if (parameters[i].HasDefaultValue) { paramValues[i] = serviceProvider.GetService(parameters[i].ParameterType); } else { paramValues[i] = serviceProvider.GetRequiredService(parameters[i].ParameterType); } } else if (parameters[i].ParameterType == typeof(CancellationToken)) { paramValues[i] = cancellationToken; } else if (parameters[i].ParameterType == typeof(Dictionary <string, object>)) { paramValues[i] = values; } else if (parameters[i].HasDefaultValue) { paramValues[i] = parameters[i].DefaultValue; } else { string message = $"Parameter {parameters[i].Name} from command {method.Name} cannot be bound"; ConsoleEx.WriteLine(ConsoleColor.Red, message); throw new InvalidOperationException(message); } } var result = method.Invoke(replInstance, paramValues); // TODO: Catch exceptions here or in the engine? if (result is Task task && typeof(Task).IsAssignableFrom(method.ReturnType)) { return(task); } return(Task.CompletedTask); } commandBuilder.WithExecute(ExecuteAsync); }