-
-
Notifications
You must be signed in to change notification settings - Fork 8
Introduce IFalloutCommand dispatch with DI for Fallout.Cli (#392, PR 0) #448
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
ChrisonSimtian
merged 4 commits into
Fallout-build:main
from
ChrisonSimtian:cli-command-dispatch-foundation
Jul 2, 2026
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
088ed19
Introduce IFalloutCommand dispatch with DI for Fallout.Cli
ChrisonSimtian f540301
Make shared CLI helpers internal for incremental command extraction
ChrisonSimtian 49d50cb
Make Cli interfaces internal for now
ChrisonSimtian a1e9c8f
Make Cli command dispatch async and apply review conventions
ChrisonSimtian File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| { | ||
| "version": "0.2.0", | ||
| "configurations": [ | ||
| { | ||
| // Prompts you for the command/args each launch, e.g. ":get-configuration" or ":bogus". | ||
| "name": "fallout (ask for args)", | ||
| "type": "coreclr", | ||
| "request": "launch", | ||
| "preLaunchTask": "build-cli", | ||
| "program": "${workspaceFolder}/src/Fallout.Cli/bin/Debug/net10.0/Fallout.Cli.dll", | ||
| "args": "${input:falloutArgs}", | ||
| "cwd": "${workspaceFolder}", | ||
| "console": "integratedTerminal", | ||
| "stopAtEntry": false | ||
| }, | ||
| { | ||
| // Dispatch error path — prints the command manifest, no side effects. | ||
| "name": "fallout :bogus", | ||
| "type": "coreclr", | ||
| "request": "launch", | ||
| "preLaunchTask": "build-cli", | ||
| "program": "${workspaceFolder}/src/Fallout.Cli/bin/Debug/net10.0/Fallout.Cli.dll", | ||
| "args": [":bogus"], | ||
| "cwd": "${workspaceFolder}", | ||
| "console": "integratedTerminal" | ||
| }, | ||
| { | ||
| // Real command via the DelegateCommand path — read-only. | ||
| "name": "fallout :get-configuration", | ||
| "type": "coreclr", | ||
| "request": "launch", | ||
| "preLaunchTask": "build-cli", | ||
| "program": "${workspaceFolder}/src/Fallout.Cli/bin/Debug/net10.0/Fallout.Cli.dll", | ||
| "args": [":get-configuration"], | ||
| "cwd": "${workspaceFolder}", | ||
| "console": "integratedTerminal" | ||
| } | ||
| ], | ||
| "inputs": [ | ||
| { | ||
| "id": "falloutArgs", | ||
| "type": "promptString", | ||
| "description": "Arguments to pass to fallout (space-separated), e.g. ':get-configuration'", | ||
| "default": ":bogus" | ||
| } | ||
| ] | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| { | ||
| "version": "2.0.0", | ||
| "tasks": [ | ||
| { | ||
| "label": "build-cli", | ||
| "command": "dotnet", | ||
| "type": "process", | ||
| "args": [ | ||
| "build", | ||
| "${workspaceFolder}/src/Fallout.Cli/Fallout.Cli.csproj", | ||
| "/property:GenerateFullPaths=true", | ||
| "/consoleloggerparameters:NoSummary" | ||
| ], | ||
| "problemMatcher": "$msCompile" | ||
| } | ||
| ] | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
| using System.Threading.Tasks; | ||
| using Fallout.Cli.Commands; | ||
| using Fallout.Cli.Prompts; | ||
| using Fallout.Common; | ||
| using Fallout.Common.IO; | ||
| using Fallout.Common.Utilities; | ||
| using Fallout.Common.Utilities.Collections; | ||
|
|
||
| namespace Fallout.Cli; | ||
|
|
||
| /// <summary> | ||
| /// Routes a command-line invocation to the matching <see cref="IFalloutCommand"/>. Commands are | ||
| /// resolved by <see cref="IFalloutCommand.Name"/> from the registered set — dash- and | ||
| /// case-insensitively, preserving every spelling the historical reflection dispatch accepted | ||
| /// (e.g. <c>:add-package</c> and <c>:addpackage</c>, <c>:PopDirectory</c> and <c>:popdirectory</c>). | ||
| /// </summary> | ||
| internal sealed class CommandDispatcher | ||
| { | ||
| private const char CommandPrefix = ':'; | ||
|
|
||
| private readonly IReadOnlyList<IFalloutCommand> commands; | ||
| private readonly IConsolePrompts prompts; | ||
|
|
||
| public CommandDispatcher(IEnumerable<IFalloutCommand> commands, IConsolePrompts prompts) | ||
| { | ||
| this.commands = commands.ToList(); | ||
| this.prompts = prompts; | ||
| } | ||
|
|
||
| public async Task<int> DispatchAsync(string[] args, AbsolutePath rootDirectory, AbsolutePath buildScript) | ||
| { | ||
| var hasCommand = args.FirstOrDefault()?.StartsWithOrdinalIgnoreCase(CommandPrefix.ToString()) ?? false; | ||
| if (hasCommand) | ||
| { | ||
| var token = args.First().Trim(CommandPrefix); | ||
| if (string.IsNullOrWhiteSpace(token)) | ||
| { | ||
| Assert.Fail($"No command specified. Usage is: fallout {CommandPrefix}<command> [args]"); | ||
| } | ||
|
|
||
| var command = Resolve(token); | ||
| return await command.ExecuteAsync(args.Skip(count: 1).ToArray(), rootDirectory, buildScript); | ||
| } | ||
|
|
||
| if (rootDirectory == null) | ||
| { | ||
| return prompts.PromptForConfirmation( | ||
| $"Could not find {Constants.FalloutDirectoryName} directory/file. Do you want to setup a build?") | ||
| ? await GetRequired("setup").ExecuteAsync(Array.Empty<string>(), rootDirectory: null, buildScript: null) | ||
| : 0; | ||
| } | ||
|
|
||
| // TODO: docker | ||
|
|
||
| return await GetRequired("run").ExecuteAsync(args, rootDirectory, BuildProjectResolver.Resolve(rootDirectory)); | ||
| } | ||
|
|
||
| private IFalloutCommand Resolve(string token) | ||
| { | ||
| return commands.SingleOrDefault(x => Normalize(x.Name).EqualsOrdinalIgnoreCase(Normalize(token))) | ||
| .NotNull(new[] { $"Command '{token}' is not supported, available commands are:" } | ||
| .Concat(commands.Select(x => $" - {x.Name}").OrderBy(x => x)).JoinNewLine()); | ||
| } | ||
|
|
||
| private IFalloutCommand GetRequired(string name) | ||
| => commands.Single(x => x.Name.EqualsOrdinalIgnoreCase(name)); | ||
|
|
||
| private static string Normalize(string value) => value.Replace("-", string.Empty); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| using System; | ||
| using System.Threading.Tasks; | ||
| using Fallout.Common.IO; | ||
|
|
||
| namespace Fallout.Cli.Commands; | ||
|
|
||
| /// <summary> | ||
| /// Transitional adapter that exposes a not-yet-extracted <c>Program.X</c> handler as an | ||
| /// <see cref="IFalloutCommand"/>, so the dispatcher can route every command uniformly through the | ||
| /// registry while the per-command conversion (issue #392) lands one PR at a time. Each conversion | ||
| /// replaces one registration of this adapter with a real command type; the adapter is deleted once | ||
| /// the last legacy handler is gone. | ||
| /// </summary> | ||
| internal sealed class DelegateCommand : IFalloutCommand | ||
| { | ||
| private readonly Func<string[], AbsolutePath, AbsolutePath, int> handler; | ||
|
|
||
| public DelegateCommand(string name, Func<string[], AbsolutePath, AbsolutePath, int> handler) | ||
| { | ||
| Name = name; | ||
| this.handler = handler; | ||
| } | ||
|
|
||
| public string Name { get; } | ||
|
|
||
| // The adapted legacy handlers are still synchronous; each #392 conversion replaces one | ||
| // registration of this adapter with a command type that overrides ExecuteAsync for real. | ||
| public Task<int> ExecuteAsync(string[] args, AbsolutePath rootDirectory, AbsolutePath buildScript) | ||
| => Task.FromResult(handler(args, rootDirectory, buildScript)); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| using System.Threading.Tasks; | ||
| using Fallout.Common.IO; | ||
|
|
||
| namespace Fallout.Cli.Commands; | ||
|
|
||
| /// <summary> | ||
| /// A single global-tool command (e.g. <c>fallout :run</c>, <c>fallout :setup</c>). | ||
| /// One command = one type, resolved by <see cref="Name"/> from the dependency-injection | ||
| /// container — replacing the historical reflection-over-<c>Program</c> dispatch. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// This surface is intentionally minimal. It is <b>not</b> a stable public plugin contract yet; | ||
| /// when a public command SDK lands (milestone #7) the API will be annotated and versioned | ||
| /// explicitly. Until then, treat additions here as internal-by-convention. | ||
| /// </remarks> | ||
| internal interface IFalloutCommand | ||
| { | ||
| /// <summary> | ||
| /// The command name as typed after the <c>:</c> prefix (prefer dash form, e.g. <c>"add-package"</c>).\ | ||
| /// Matched case-insensitively and with dashes ignored (so <c>:addpackage</c> also matches).\ | ||
| /// Legacy commands may still use PascalCase names (e.g. <c>"GetNextDirectory"</c>). | ||
| string Name { get; } | ||
|
|
||
| /// <summary> | ||
| /// Executes the command and returns the process exit code. | ||
| /// </summary> | ||
| /// <param name="args">The arguments following the command token.</param> | ||
| /// <param name="rootDirectory">The resolved repository root, or <c>null</c> when none was found.</param> | ||
| /// <param name="buildScript">The resolved build script / project file, or <c>null</c> when none applies.</param> | ||
| Task<int> ExecuteAsync(string[] args, AbsolutePath rootDirectory, AbsolutePath buildScript); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.