diff --git a/ForceOps.Benchmarks/ForceOps.Benchmarks.csproj b/ForceOps.Benchmarks/ForceOps.Benchmarks.csproj index 6e7e282..16322a9 100644 --- a/ForceOps.Benchmarks/ForceOps.Benchmarks.csproj +++ b/ForceOps.Benchmarks/ForceOps.Benchmarks.csproj @@ -4,7 +4,7 @@ - + diff --git a/ForceOps.Lib/ForceOps.Lib.csproj b/ForceOps.Lib/ForceOps.Lib.csproj index 9979817..dcb8b83 100644 --- a/ForceOps.Lib/ForceOps.Lib.csproj +++ b/ForceOps.Lib/ForceOps.Lib.csproj @@ -15,7 +15,7 @@ - - + + \ No newline at end of file diff --git a/ForceOps.Test/ForceOps.Test.csproj b/ForceOps.Test/ForceOps.Test.csproj index be89e41..4127f47 100644 --- a/ForceOps.Test/ForceOps.Test.csproj +++ b/ForceOps.Test/ForceOps.Test.csproj @@ -5,11 +5,11 @@ - - - + + + - + runtime; build; native; contentfiles; analyzers all diff --git a/ForceOps/ForceOps.csproj b/ForceOps/ForceOps.csproj index f9b46ef..7eced39 100644 --- a/ForceOps/ForceOps.csproj +++ b/ForceOps/ForceOps.csproj @@ -26,7 +26,7 @@ - + diff --git a/ForceOps/src/Program.cs b/ForceOps/src/Program.cs index 24d98bc..ea1f8b4 100644 --- a/ForceOps/src/Program.cs +++ b/ForceOps/src/Program.cs @@ -1,6 +1,5 @@ -using System.CommandLine; -using System.CommandLine.Builder; -using System.CommandLine.Parsing; +using System.CommandLine; +using System.CommandLine.Help; using System.Runtime.Versioning; namespace ForceOps; @@ -21,48 +20,46 @@ public ForceOpsRunner(ForceOps forceOps) internal int Run() { - var rootCommand = new RootCommand("By hook or by crook, perform operations on files and directories. If they are in use by a process, kill the process.") - { - Name = "forceops" - }; - rootCommand.AddCommand(CreateDeleteCommand()); - rootCommand.AddCommand(CreateListCommand()); + // A named Command is used instead of RootCommand so the usage text shows + // "forceops" rather than the executable name ("ForceOps"). + var rootCommand = new Command("forceops", "By hook or by crook, perform operations on files and directories. If they are in use by a process, kill the process."); + rootCommand.Options.Add(new HelpOption()); + rootCommand.Options.Add(new VersionOption()); + rootCommand.Subcommands.Add(CreateDeleteCommand()); + rootCommand.Subcommands.Add(CreateListCommand()); - var parser = new CommandLineBuilder(rootCommand) - .UseDefaults() - .AddMiddleware(async (context, next) => + try + { + return rootCommand.Parse(_forceOps.args).Invoke(new InvocationConfiguration { - try - { - await next(context); - } - catch (Exception ex) - { - _forceOps.caughtException = ex; - - if (ex is FileNotFoundException fileNotFoundEx) - { - _forceOps.forceOpsContext.environmentExit.Exit((int)ExitCode.FileNotFound, ex.Message); - } - throw; - } - }) - .Build(); + EnableDefaultExceptionHandler = false + }); + } + catch (Exception ex) + { + _forceOps.caughtException = ex; - return parser.Invoke(_forceOps.args); + if (ex is FileNotFoundException) + { + _forceOps.forceOpsContext.environmentExit.Exit((int)ExitCode.FileNotFound, ex.Message); + } + Console.Error.WriteLine(ex.ToString()); + return 1; + } } Command CreateDeleteCommand() { - var filesToDeleteArgument = new Argument("files", "Files or directories to delete.") + var filesToDeleteArgument = new Argument("files") { + Description = "Files or directories to delete.", Arity = ArgumentArity.OneOrMore }; - var forceOption = new Option(["-f", "--force"], "Ignore nonexistent files and arguments."); - var disableElevate = new Option(["-e", "--disable-elevate"], "Do not attempt to elevate if the file can't be deleted."); - var retryDelay = new Option(["-d", "--retry-delay"], () => 50, "Delay when retrying to delete a file, after deleting processes holding a lock."); - var maxRetries = new Option(["-n", "--max-retries"], () => 10, "Number of retries when deleting a locked file."); + var forceOption = new Option("--force", "-f") { Description = "Ignore nonexistent files and arguments." }; + var disableElevate = new Option("--disable-elevate", "-e") { Description = "Do not attempt to elevate if the file can't be deleted." }; + var retryDelay = new Option("--retry-delay", "-d") { DefaultValueFactory = _ => 50, Description = "Delay when retrying to delete a file, after deleting processes holding a lock." }; + var maxRetries = new Option("--max-retries", "-n") { DefaultValueFactory = _ => 10, Description = "Number of retries when deleting a locked file." }; var deleteCommand = new Command("delete", "Delete files or a directories recursively.") { @@ -73,20 +70,28 @@ Command CreateDeleteCommand() maxRetries }; - deleteCommand.AddAlias("rm"); - deleteCommand.AddAlias("remove"); - deleteCommand.SetHandler(_forceOps.DeleteCommand, filesToDeleteArgument, forceOption, disableElevate, retryDelay, maxRetries); + deleteCommand.Aliases.Add("rm"); + deleteCommand.Aliases.Add("remove"); + deleteCommand.SetAction(parseResult => _forceOps.DeleteCommand( + parseResult.GetValue(filesToDeleteArgument)!, + parseResult.GetValue(forceOption), + parseResult.GetValue(disableElevate), + parseResult.GetValue(retryDelay), + parseResult.GetValue(maxRetries))); return deleteCommand; } Command CreateListCommand() { - var fileOrDirectoryArgument = new Argument("fileOrDirectory", "File or directory to get the locks of."); + var fileOrDirectoryArgument = new Argument("fileOrDirectory") + { + Description = "File or directory to get the locks of." + }; var listCommand = new Command("list", "Uses LockCheck to output processes using a file or directory.") { fileOrDirectoryArgument }; - listCommand.SetHandler(_forceOps.ListCommand, fileOrDirectoryArgument); + listCommand.SetAction(parseResult => _forceOps.ListCommand(parseResult.GetValue(fileOrDirectoryArgument)!)); return listCommand; } }