using McMaster.Extensions.CommandLineUtils;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using System.Collections.Generic;
using System.Threading.Tasks;
abstract class CLICommandBase
protected abstract Task<int> DoWorkAsync(CommandLineApplication app);
protected Task<int> OnExecuteAsync(CommandLineApplication app) => DoWorkAsync(app);
[Command(Name = "process", UnrecognizedArgumentHandling = UnrecognizedArgumentHandling.CollectAndContinue, OptionsComparison = StringComparison.InvariantCultureIgnoreCase)]
[Subcommand(typeof(List1ProcessCommand))]
[Subcommand(typeof(List2ProcessCommand))]
class ProcessCommand : CLICommandBase
protected async override Task<int> DoWorkAsync(CommandLineApplication app) {
[Command(Name = "list1", UnrecognizedArgumentHandling = UnrecognizedArgumentHandling.CollectAndContinue, OptionsComparison = StringComparison.InvariantCultureIgnoreCase)]
class List1ProcessCommand : CLICommandBase
private readonly ICommandHandler _handler;
private readonly IFactory<IFileProvider, string> _fileProviderFactory;
public List1ProcessCommand(ICommandHandler handler, IFactory<IFileProvider, string> fpFactory) {
_fileProviderFactory = fpFactory;
[Option(CommandOptionType.SingleValue, LongName = "folder")]
public string InputFolder { get; }
protected override async Task<int> DoWorkAsync(CommandLineApplication app)
Console.WriteLine($"Executing list1 in {InputFolder}");
var fileProvider = _fileProviderFactory.Create(InputFolder);
var fileInfos = fileProvider.GetDirectoryContents("");
foreach(var finfo in fileInfos)
[Command(Name = "list2", UnrecognizedArgumentHandling = UnrecognizedArgumentHandling.CollectAndContinue, OptionsComparison = StringComparison.InvariantCultureIgnoreCase)]
class List2ProcessCommand : CLICommandBase
private readonly ICommandHandler _handler;
private readonly IFileSystem _fileSystem;
public List2ProcessCommand(ICommandHandler handler, IFileSystem fileSystem) {
_fileSystem = fileSystem;
[Option(CommandOptionType.SingleValue, LongName = "folder")]
public string InputFolder { get; }
protected override async Task<int> DoWorkAsync(CommandLineApplication app)
Console.WriteLine($"Executing list2 in {InputFolder}");
var fileInfos = _fileSystem.GetDirectoryContents(InputFolder, "");
foreach(var finfo in fileInfos)
public interface IFactory<TInterface, TOptions>
TInterface Create(TOptions options);
public class DefaultFileProviderFactory : IFactory<IFileProvider, string>
public IFileProvider Create(string root) => new PhysicalFileProvider(root);
public interface IFileSystem
IEnumerable<IFileInfo> GetDirectoryContents(string baseFolder, string path);
public class DefaultFileSystem : IFileSystem
private readonly IFactory<IFileProvider, string> _fileProviderFactory;
public DefaultFileSystem(IFactory<IFileProvider, string> fpFactory) {
_fileProviderFactory = fpFactory;
public IEnumerable<IFileInfo> GetDirectoryContents(string baseFolder, string path)
var fileProvider = _fileProviderFactory.Create(baseFolder);
return fileProvider.GetDirectoryContents(path);
public interface ICommandHandler {
public void Execute(IFileInfo fileInfo);
public class ListFullPathFileHandler : ICommandHandler {
public void Execute(IFileInfo fileInfo) {
Console.WriteLine($"ListFullPath {fileInfo.PhysicalPath}");
public class ListNameFileHandler : ICommandHandler {
public void Execute(IFileInfo fileInfo) {
Console.WriteLine($"ListName {fileInfo.Name}");
private static async Task<int> Main()
var builder = new HostBuilder().ConfigureServices(ConfigureApplicationServices);
string[] args = new string[] { "process", "list2", "--folder", "/" };
return await builder.RunCommandLineApplicationAsync<ProcessCommand>(args).ConfigureAwait(false);
Console.WriteLine(ex.Message);
static void ConfigureApplicationServices(HostBuilderContext hostContext, IServiceCollection services)
services.AddSingleton<IFactory<IFileProvider, string>, DefaultFileProviderFactory>();
services.AddSingleton<IFileSystem, DefaultFileSystem>();
services.AddTransient<ICommandHandler, ListFullPathFileHandler>();
services.AddTransient<ICommandHandler, ListNameFileHandler>();