public void Modify_an_existing_instance_should_keep_all_default_values_if_no_argument_matches_option() { var parser = new Parser(new Command("the-command")); var instance = new ClassWithComplexTypes(); var bindingContext = new BindingContext(parser.Parse("the-command")); var binder = new ModelBinder(typeof(ClassWithComplexTypes)); binder.UpdateInstance(instance, bindingContext); instance.Should().BeEquivalentTo(new ClassWithComplexTypes()); }
public void Parse_result_can_be_used_to_modify_an_existing_instance_without_doing_handler_invocation() { var parser = new Parser(new Command("the-command") { new Option <int>("--int-option") }); var instance = new ClassWithMultiLetterSetters(); var bindingContext = new BindingContext(parser.Parse("the-command --int-option 123")); var binder = new ModelBinder(typeof(ClassWithMultiLetterSetters)); binder.UpdateInstance(instance, bindingContext); instance.IntOption.Should().Be(123); }
public virtual Task <int> Bind(InvocationContext context) { if (context == null) { return(Task.FromResult(1)); } var cancelToken = context.GetCancellationToken(); if (cancelToken.IsCancellationRequested) { return(Task.FromCanceled <int>(cancelToken)); } ModelBinder.UpdateInstance(Target, context.BindingContext); return(Task.FromResult(0)); }
/// <summary> /// Adapts System.CommandLine to our command class and hosting conventions. /// </summary> /// <param name="builder">The command builder instance.</param> /// <typeparam name="TCommand">The command to bind to.</typeparam> /// <typeparam name="THandler">The handler to invoke.</typeparam> /// <returns>The same command builder instance.</returns> public static IHostBuilder UseEmuCommand <TCommand, THandler>(this IHostBuilder builder) where TCommand : Command where THandler : EmuCommandHandler { // adapted from: https://github.com/dotnet/command-line-api/blob/43be76901630aae866657dd5ec1978a5d48d5b09/src/System.CommandLine.Hosting/HostingExtensions.cs#L85 var commandType = typeof(TCommand); var handlerType = typeof(THandler); if (!typeof(Command).IsAssignableFrom(commandType)) { throw new ArgumentException($"{nameof(commandType)} must be a type of {nameof(Command)}", nameof(THandler)); } if (!typeof(ICommandHandler).IsAssignableFrom(handlerType)) { throw new ArgumentException($"{nameof(handlerType)} must implement {nameof(ICommandHandler)}", nameof(THandler)); } // only register services for the current command! if (builder.Properties[typeof(InvocationContext)] is InvocationContext invocation && invocation.ParseResult.CommandResult.Command is Command command && command.GetType() == commandType) { invocation.BindingContext.AddService <THandler>( c => c.GetRequiredService <IHost>().Services.GetRequiredService <THandler>()); //command.Handler = CommandHandler.Create(handlerType.GetMethod(nameof(ICommandHandler.InvokeAsync))); command.Handler = CommandHandler.Create <THandler, IHost, InvocationContext>(async(handler, host, context) => { var modelBinder = new ModelBinder <THandler>(); modelBinder.UpdateInstance(handler, invocation.BindingContext); var logger = host.Services.GetRequiredService <ILogger <THandler> >(); using (logger.Measure(command.Name, LogLevel.Debug)) { logger.LogDebug("Handler: {@args}", handler); int result = 1; try { result = await handler.InvokeAsync(context); } finally { // flush output footer handler?.Writer?.Dispose(); } if (logger.IsEnabled(LogLevel.Information)) { // Hack: flush a new line so that any stdout that was just written is delmitted a little bit Console.Error.WriteLine(); } return(result); } }); builder.ConfigureServices((collection) => { // the "current command" an alias for the base type collection.AddTransient <Command, TCommand>(); // the handler collection.AddTransient <THandler>(); // but also add a alias for the base type collection.AddTransient <EmuCommandHandler, THandler>(); }); } return(builder); }