public GraphController() { var query = new RootQuery(); Schema = new RootSchema(query); Executor = new DocumentExecuter(); }
private int?Execute(CliContext context) { RootSchema root = context.RootSchema; CommandInput input = context.Input; // Try to get the command matching the input or fallback to default CommandSchema commandSchema = root.TryFindCommand(input.CommandName) ?? StubDefaultCommand.Schema; // TODO: is the problem below still valid? // TODO: is it poossible to overcome this (related to [!]) limitation of new mode system // Forbid to execute real default command in interactive mode without [!] directive. //if (!(commandSchema.IsHelpOptionAvailable && input.IsHelpOptionSpecified) && // _applicationLifetime.CurrentModeType == typeof(InteractiveMode) && // commandSchema.IsDefault && !hasDefaultDirective) //{ // commandSchema = StubDefaultCommand.Schema; //} // Update CommandSchema context.CommandSchema = commandSchema; // Get command instance (default values are used in help so we need command instance) ICommand instance = GetCommandInstance(commandSchema); context.Command = instance; // To avoid instantiating the command twice, we need to get default values // before the arguments are bound to the properties IReadOnlyDictionary <ArgumentSchema, object?> defaultValues = commandSchema.GetArgumentValues(instance); context.CommandDefaultValues = defaultValues; return(null); }
/// <inheritdoc/> public void Write(CommandSchema command, IReadOnlyDictionary <ArgumentSchema, object?> defaultValues) { RootSchema root = _context.RootSchema; IEnumerable <CommandSchema> childCommands = root.GetChildCommands(command.Name) .OrderBy(x => x.Name); _console.ResetColor(); _console.ForegroundColor = ConsoleColor.Gray; if (command.IsDefault) { WriteApplicationInfo(); } WriteCommandDescription(command); WriteCommandUsage(root.Directives, command, childCommands); WriteCommandParameters(command); WriteCommandOptions(command, defaultValues); WriteModeRestrictionsManual(command); WriteCommandChildren(command, childCommands); WriteDirectivesManual(root.Directives); WriteCommandManual(command); WriteLine(); }
/// <summary> /// Parses input before pipeline execution. /// </summary> protected virtual async Task <int> ParseInput(IReadOnlyList <string> commandLineArguments, RootSchema root) { //TODO: CommandInput.Parse as middleware step CommandInput input = CommandInputResolver.Parse(commandLineArguments, root.GetCommandNames()); CliContext.Input = input; return(await ExecuteCommand()); }
public static RootSchema GetSchema() { RootSchema schema = new RootSchema().InitProperties(); schema.AdditionalProperties = false; foreach (var extension in ExtensionInfo.Extensions) { schema.Properties.Add(extension.Name, new RootSchema() { Type = SchemaObjectType.Boolean, Default = false, Description = "The '" + extension.Name + "' extension costs " + extension.Cost + " extension points." }); } return(schema); }
private int?Execute(CliContext context) { RootSchema root = context.RootSchema; CommandInput input = context.Input; // Try to get the command matching the input or fallback to default bool hasDefaultDirective = input.HasDirective(BuiltInDirectives.Default); CommandSchema command = root.TryFindCommand(input.CommandName, hasDefaultDirective) ?? StubDefaultCommand.Schema; // Forbid to execute real default command in interactive mode without [!] directive. if (!(command.IsHelpOptionAvailable && input.IsHelpOptionSpecified) && context.IsInteractiveMode && command.IsDefault && !hasDefaultDirective) { command = StubDefaultCommand.Schema; } // Update CommandSchema context.CommandSchema = command; return(null); }
private async Task RunInteractivelyAsync(RootSchema root) { IConsole console = CliContext.Console; string executableName = CliContext.Metadata.ExecutableName; do { string[]? commandLineArguments = GetInput(console, executableName); if (commandLineArguments is null) { console.ResetColor(); return; } CommandInput input = CommandInputResolver.Parse(commandLineArguments, root.GetCommandNames()); CliContext.Input = input; //TODO maybe refactor with some clever IDisposable class await ExecuteCommand(); console.ResetColor(); }while (!console.GetCancellationToken().IsCancellationRequested); }
protected override async Task <int> ParseInput(IReadOnlyList <string> commandLineArguments, RootSchema root) { CommandInput input = CommandInputResolver.Parse(commandLineArguments, root.GetCommandNames()); CliContext.Input = input; if (input.IsInteractiveDirectiveSpecified) { CliContext.IsInteractiveMode = true; // we don't want to run default command for e.g. `[interactive]` but we want to run if there is sth else if (!input.IsDefaultCommandOrEmpty) { await ExecuteCommand(); } await RunInteractivelyAsync(root); return(ExitCodes.Success); // called after Ctrl+C } return(await ExecuteCommand()); }
/// <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)); } }
public async ValueTask <int> RunAsync(IEnumerable <string> commandLineArguments, IReadOnlyDictionary <string, string> environmentVariables) { try { _logger.LogInformation("Starting CLI application..."); _console.ResetColor(); _environmentVariablesAccessor.EnvironmentVariables = environmentVariables; RootSchema rootSchema = _rootSchemaAccessor.RootSchema; //Force root schema to resolve. TODO: find a solution to enable lazy root schema resolving. //TODO: OnStart() _startupMessage?.Invoke(_metadata, _console); int exitCode = await StartAppAsync(commandLineArguments); //TODO: OnStop() _logger.LogInformation("CLI application stopped."); return(exitCode); } // This may throw pre-execution resolving exceptions which are useful only to the end-user catch (TypinException ex) { _logger.LogDebug(ex, $"{nameof(TypinException)} occured. Trying to find exception handler."); IEnumerable <ICliExceptionHandler> exceptionHandlers = _serviceProvider.GetServices <ICliExceptionHandler>(); foreach (ICliExceptionHandler handler in exceptionHandlers) { if (handler.HandleException(ex)) { _logger.LogDebug(ex, "Exception handled by {ExceptionHandlerType}.", handler.GetType().FullName); break; } } _logger.LogCritical(ex, "Unhandled Typin exception caused app to terminate."); _console.Error.WithForegroundColor(ConsoleColor.DarkRed, (error) => error.WriteLine($"Unhandled Typin exception caused app to terminate.")); _console.Error.WriteLine(); _console.Error.WriteException(ex); return(ExitCodes.FromException(ex)); } // 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) { _logger.LogCritical(ex, "Unhandled exception caused app to terminate."); _console.Error.WithForegroundColor(ConsoleColor.DarkRed, (error) => error.WriteLine($"Fatal error occured in {_metadata.ExecutableName}.")); _console.Error.WriteLine(); _console.Error.WriteException(ex); return(ExitCodes.FromException(ex)); } }
public RootSchemaTests() { var serviceProvider = Mock.Of <IServiceProvider>(); _rootSchema = new RootSchema(serviceProvider); }