Esempio n. 1
0
    /// <summary>
    /// Runs the application with the specified command line arguments and environment variables.
    /// Returns an exit code which indicates whether the application completed successfully.
    /// </summary>
    /// <remarks>
    /// When running WITHOUT debugger (i.e. in production), this method swallows all exceptions and
    /// reports them to the console.
    /// </remarks>
    public async ValueTask <int> RunAsync(
        IReadOnlyList <string> commandLineArguments,
        IReadOnlyDictionary <string, string> environmentVariables)
    {
        try
        {
            // Console colors may have already been overriden by the parent process,
            // so we need to reset it to make sure that everything we write looks properly.
            _console.ResetColor();

            var applicationSchema = ApplicationSchema.Resolve(Configuration.CommandTypes);

            var commandInput = CommandInput.Parse(
                commandLineArguments,
                environmentVariables,
                applicationSchema.GetCommandNames()
                );

            return(await RunAsync(applicationSchema, commandInput));
        }
        // To prevent the app from showing the annoying troubleshooting dialog on Windows,
        // we handle all exceptions ourselves and print them to the console.
        //
        // We only want to do that if the app is running in production, which we infer
        // based on whether a debugger is attached to the process.
        //
        // When not running in production, we want the IDE to show exceptions to the
        // developer, so we don't swallow them in that case.
        catch (Exception ex) when(!Debugger.IsAttached)
        {
            _console.Error.WriteException(ex);
            return(1);
        }
    }
        public void Positive_ReadMoreNamedParameters()
        {
            var keyword         = "keyword";
            var parameter1Name  = "name";
            var parameter1Value = "value";
            var parameter2Name  = "name";
            var parameter2Value = "value";
            var parameter3Name  = "name";
            var parameter3Value = "value";
            var line            =
                $"{keyword} -{parameter1Name}:{parameter1Value} -{parameter2Name}:{parameter2Value} -{parameter3Name}:{parameter3Value} ";

            var result = CommandInput.Parse(line);

            Assert.IsNotNull(result);
            Assert.AreEqual(keyword, result.Keyword);
            Assert.IsNotNull(result.Parameters);
            Assert.AreEqual(3, result.Parameters.Count);
            Assert.IsTrue(result.Parameters[0].IsNamed);
            Assert.AreEqual(parameter1Name, result.Parameters[0].Name);
            Assert.AreEqual(parameter1Value, result.Parameters[0].Value);
            Assert.IsTrue(result.Parameters[1].IsNamed);
            Assert.AreEqual(parameter2Name, result.Parameters[1].Name);
            Assert.AreEqual(parameter2Value, result.Parameters[1].Value);
            Assert.IsTrue(result.Parameters[2].IsNamed);
            Assert.AreEqual(parameter3Name, result.Parameters[2].Name);
            Assert.AreEqual(parameter3Value, result.Parameters[2].Value);
        }
Esempio n. 3
0
        internal void Directive_can_be_enabled_by_specifying_its_name_in_square_brackets(IReadOnlyList <string> arguments, CommandInput expectedInput)
        {
            // Arrange
            var commandNames = Array.Empty <string>();

            // Act
            var input = CommandInput.Parse(arguments, commandNames);

            // Assert
            input.Should().BeEquivalentTo(expectedInput);
        }
Esempio n. 4
0
        internal void Command_name_is_matched_from_arguments_that_come_before_parameters(
            IReadOnlyList <string> commandNames,
            IReadOnlyList <string> arguments,
            CommandInput expectedInput)
        {
            // Act
            var input = CommandInput.Parse(arguments, commandNames);

            // Assert
            input.Should().BeEquivalentTo(expectedInput);
        }
Esempio n. 5
0
        internal void Parameter_can_be_set_by_specifying_the_value_directly(IReadOnlyList <string> arguments, CommandInput expectedInput)
        {
            // Arrange
            var commandNames = Array.Empty <string>();

            // Act
            var input = CommandInput.Parse(arguments, commandNames);

            // Assert
            input.Should().BeEquivalentTo(expectedInput);
        }
Esempio n. 6
0
        internal void Option_can_be_set_by_specifying_its_short_name_after_a_single_dash(IReadOnlyList <string> arguments, CommandInput expectedInput)
        {
            // Arrange
            var commandNames = Array.Empty <string>();

            // Act
            var input = CommandInput.Parse(arguments, commandNames);

            // Assert
            input.Should().BeEquivalentTo(expectedInput);
        }
        public void Positive_ReadOnlyKeyword()
        {
            var keyword = "keyword";

            var result = CommandInput.Parse(keyword);

            Assert.IsNotNull(result);
            Assert.AreEqual(keyword, result.Keyword);
            Assert.IsNotNull(result.Parameters);
            Assert.AreEqual(0, result.Parameters.Count);
        }
        public void Positive_ReadEmpty()
        {
            var line = "";

            var result = CommandInput.Parse(line);

            Assert.IsNotNull(result);
            Assert.AreEqual(string.Empty, result.Keyword);
            Assert.IsNotNull(result.Parameters);
            Assert.AreEqual(0, result.Parameters.Count);
        }
Esempio n. 9
0
        public static bool TestLine <T>(TypedSignature <T> sig, string line) where T : class, new()
        {
            var ci  = CommandInput.Parse(line);
            var can = sig.CanRun(ci);
            var run = false;

            if (can)
            {
                run = sig.Run(ci, new List <CommandError>());
            }
            return(can && run);
        }
Esempio n. 10
0
        public void Input_is_empty_if_no_arguments_are_provided()
        {
            // Arrange
            var arguments    = Array.Empty <string>();
            var commandNames = Array.Empty <string>();

            // Act
            var input = CommandInput.Parse(arguments, commandNames);

            // Assert
            input.Should().BeEquivalentTo(CommandInput.Empty);
        }
        public void Positive_ReadSingleIndexParameter()
        {
            var keyword        = "keyword";
            var parameterValue = "value";
            var line           = $"{keyword} {parameterValue}";

            var result = CommandInput.Parse(line);

            Assert.IsNotNull(result);
            Assert.AreEqual(keyword, result.Keyword);
            Assert.IsNotNull(result.Parameters);
            Assert.AreEqual(1, result.Parameters.Count);
            Assert.IsFalse(result.Parameters[0].IsNamed);
            Assert.AreEqual(parameterValue, result.Parameters[0].Value);
        }
        public void Positive_ReadSingleSwitchParameters()
        {
            var keyword        = "keyword";
            var parameter1Name = "name";
            var line           = $"{keyword} -{parameter1Name}";

            var result = CommandInput.Parse(line);

            Assert.IsNotNull(result);
            Assert.AreEqual(keyword, result.Keyword);
            Assert.IsNotNull(result.Parameters);
            Assert.AreEqual(1, result.Parameters.Count);

            Assert.IsTrue(result.Parameters[0].IsNamed);
            Assert.AreEqual(null, result.Parameters[0].Value);
        }
Esempio n. 13
0
        /// <summary>
        /// Runs the application with specified command line arguments and environment variables, and returns the exit code.
        /// </summary>
        /// <remarks>
        /// If a <see cref="CommandException"/> is thrown during command execution, it will be handled and routed to the console.
        /// Additionally, if the debugger is not attached (i.e. the app is running in production), all other exceptions thrown within
        /// this method will be handled and routed to the console as well.
        /// </remarks>
        public async ValueTask <int> RunAsync(
            IReadOnlyList <string> commandLineArguments,
            IReadOnlyDictionary <string, string> environmentVariables)
        {
            try
            {
                var root  = RootSchema.Resolve(_configuration.CommandTypes);
                var input = CommandInput.Parse(commandLineArguments, root.GetCommandNames());

                // Debug mode
                if (_configuration.IsDebugModeAllowed && input.IsDebugDirectiveSpecified)
                {
                    await LaunchAndWaitForDebuggerAsync();
                }

                // Preview mode
                if (_configuration.IsPreviewModeAllowed && input.IsPreviewDirectiveSpecified)
                {
                    WriteCommandLineInput(input);
                    return(ExitCode.Success);
                }

                // Try to get the command matching the input or fallback to default
                var command =
                    root.TryFindCommand(input.CommandName) ??
                    root.TryFindDefaultCommand() ??
                    StubDefaultCommand.Schema;

                // Version option
                if (command.IsVersionOptionAvailable && input.IsVersionOptionSpecified)
                {
                    _console.Output.WriteLine(_metadata.VersionText);
                    return(ExitCode.Success);
                }

                // Get command instance (also used in help text)
                var instance = GetCommandInstance(command);

                // To avoid instantiating the command twice, we need to get default values
                // before the arguments are bound to the properties
                var defaultValues = command.GetArgumentValues(instance);

                // Help option
                if (command.IsHelpOptionAvailable && input.IsHelpOptionSpecified ||
                    command == StubDefaultCommand.Schema && !input.Parameters.Any() && !input.Options.Any())
                {
                    _helpTextWriter.Write(root, command, defaultValues);
                    return(ExitCode.Success);
                }

                // Bind arguments
                try
                {
                    command.Bind(instance, input, environmentVariables);
                }
                // This may throw exceptions which are useful only to the end-user
                catch (CliFxException ex)
                {
                    WriteError(ex.ToString());
                    _helpTextWriter.Write(root, command, defaultValues);

                    return(ExitCode.FromException(ex));
                }

                // Execute the command
                try
                {
                    await instance.ExecuteAsync(_console);

                    return(ExitCode.Success);
                }
                // Swallow command exceptions and route them to the console
                catch (CommandException ex)
                {
                    WriteError(ex.ToString());

                    if (ex.ShowHelp)
                    {
                        _helpTextWriter.Write(root, command, defaultValues);
                    }

                    return(ex.ExitCode);
                }
            }
            // To prevent the app from showing the annoying Windows troubleshooting dialog,
            // we handle all exceptions and route them to the console nicely.
            // However, we don't want to swallow unhandled exceptions when the debugger is attached,
            // because we still want the IDE to show them to the developer.
            catch (Exception ex) when(!Debugger.IsAttached)
            {
                WriteError(ex.ToString());
                return(ExitCode.FromException(ex));
            }
        }