예제 #1
0
        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());
        }
예제 #2
0
        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);
        }