Skip to content

Commit

Permalink
Merge pull request #29 from dotnet-campus/t/lvyi/gnu
Browse files Browse the repository at this point in the history
支持为命令行添加过滤器(这样能全局处理某些选项)
  • Loading branch information
kkwpsv authored Jul 30, 2020
2 parents 67f160e + 7cf27c8 commit ab06c31
Show file tree
Hide file tree
Showing 41 changed files with 1,161 additions and 409 deletions.
2 changes: 1 addition & 1 deletion build/Version.props
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<Project>
<PropertyGroup>
<Version>3.2.1</Version>
<Version>3.3.0</Version>
</PropertyGroup>
</Project>
21 changes: 21 additions & 0 deletions samples/dotnetCampus.CommandLine.Sample/DefaultOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;
using System.IO;

using dotnetCampus.Cli.Properties;

namespace dotnetCampus.Cli
{
internal class DefaultOptions
{
[Option(LocalizableDescription = nameof(LocalizableStrings.SamplePropertyDescription))]
public string? DefaultText { get; set; }

[Option(LocalizableDescription = nameof(LocalizableStrings.SampleDirectoryPropertyDescription))]
public DirectoryInfo? DefaultDirectory { get; set; }

internal void Run()
{
Console.WriteLine("默认行为执行……");
}
}
}
1 change: 1 addition & 0 deletions samples/dotnetCampus.CommandLine.Sample/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ static void Main(string[] args)
{
CommandLine.Parse(args, LocalizableStrings.ResourceManager)
.AddStandardHandlers()
.AddHandler<DefaultOptions>(o => o.Run())
.AddHandler<SampleOptions>(o => o.Run())
.Run();
}
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,12 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="SampleDirectoryPropertyDescription" xml:space="preserve">
<value>Pass any directory into this option.</value>
</data>
<data name="SampleFilePropertyDescription" xml:space="preserve">
<value>Pass any file into this option.</value>
</data>
<data name="SamplePropertyDescription" xml:space="preserve">
<value>Output any text passed to this option.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,12 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="SampleDirectoryPropertyDescription" xml:space="preserve">
<value>允许传入一个目录路径。</value>
</data>
<data name="SampleFilePropertyDescription" xml:space="preserve">
<value>允许传入一个文件路径。</value>
</data>
<data name="SamplePropertyDescription" xml:space="preserve">
<value>输出传入的任何类型的字符串。</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,33 @@
"Debug": {
"commandName": "Project"
},
"--help": {
"Debug --help": {
"commandName": "Project",
"commandLineArgs": "--help"
},
"--version": {
"Debug --version": {
"commandName": "Project",
"commandLineArgs": "--version"
},
"sample": {
"Debug sample": {
"commandName": "Project",
"commandLineArgs": "sample --sample-property \"Hello World\""
},
"Debug sample --help": {
"commandName": "Project",
"commandLineArgs": "sample --help"
},
"Debug unknown": {
"commandName": "Project",
"commandLineArgs": "unknown --sample-property \"Hello World\""
},
"Debug unknown --help": {
"commandName": "Project",
"commandLineArgs": "unknown --help"
},
"Debug --options": {
"commandName": "Project",
"commandLineArgs": "--sample-property \"Hello World\""
}
}
}
7 changes: 6 additions & 1 deletion samples/dotnetCampus.CommandLine.Sample/SampleOptions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.IO;

using dotnetCampus.Cli.Properties;

Expand All @@ -8,10 +9,14 @@ namespace dotnetCampus.Cli
internal class SampleOptions
{
[Option(LocalizableDescription = nameof(LocalizableStrings.SamplePropertyDescription))]
public string? SampleProperty { get; set; }
public string? SampleText { get; set; }

[Option(LocalizableDescription = nameof(LocalizableStrings.SampleFilePropertyDescription))]
public FileInfo? SampleFile { get; set; }

internal void Run()
{
Console.WriteLine("示例行为执行……");
}
}
}
22 changes: 17 additions & 5 deletions src/dotnetCampus.CommandLine/CommandLine.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
Expand All @@ -13,6 +12,7 @@
using System.Threading.Tasks;
using System.Web;

using dotnetCampus.Cli.Compatibility;
using dotnetCampus.Cli.Core;
using dotnetCampus.Cli.StateMachine;

Expand All @@ -35,7 +35,8 @@ namespace dotnetCampus.Cli
public sealed class CommandLine : ICommandLineHandlerBuilder, IEnumerable<ListGroupItem>
{
private readonly ListGroup<SingleOptimizedStrings> _optionArgs;
private readonly List<CommandLineVerbMatch<Task<int>>> _toMatchList = new List<CommandLineVerbMatch<Task<int>>>();
private readonly List<CommandLineFilterMatch> _filterCreatorList = new List<CommandLineFilterMatch>();
private readonly List<CommandLineTypeMatcher<Task<int>>> _toMatchList = new List<CommandLineTypeMatcher<Task<int>>>();

private CommandLine(ListGroup<SingleOptimizedStrings> optionArgs, ResourceManager? resourceManager)
{
Expand All @@ -48,10 +49,15 @@ private CommandLine(ListGroup<SingleOptimizedStrings> optionArgs, ResourceManage
/// </summary>
internal ResourceManager? ResourceManager { get; }

/// <summary>
/// 收集的过滤器。
/// </summary>
internal IReadOnlyList<CommandLineFilterMatch> FilterMatchList => _filterCreatorList;

/// <summary>
/// 收集的谓词处理方法。
/// </summary>
internal IReadOnlyList<CommandLineVerbMatch<Task<int>>> ToMatchList => _toMatchList;
internal IReadOnlyList<CommandLineTypeMatcher<Task<int>>> VerbMatchList => _toMatchList;

/// <summary>
/// 自动查找命令行类型 <typeparamref name="T"/> 的解析器,然后解析出参数 <typeparamref name="T"/> 的一个新实例。
Expand Down Expand Up @@ -170,8 +176,14 @@ public T As<T>(ICommandLineOptionParser<T> parser)
return parser.Commit();
}

internal void AddMatch<TVerb>(Func<string?, MatchHandleResult<Task<int>>> match)
=> _toMatchList.Add(new CommandLineVerbMatch<Task<int>>(typeof(TVerb), match));
internal void AddMatch(CommandLineFilterMatch filterCreator)
=> _filterCreatorList.Add(filterCreator);

internal void AddMatch<TVerb>(CommandLineTypeMatcher<Task<int>> matcher)
=> _toMatchList.Add(matcher);

//internal void AddMatch<TVerb>(Func<string?, CommandLineTypeMatchResult<Task<int>>> match)
// => _toMatchList.Add(new CommandLineTypeMatcher<Task<int>>(typeof(TVerb), match));

/// <summary>
/// 将命令行参数转换为字符串值的字典。Key 为选项,Value 为选项后面的值。
Expand Down
16 changes: 2 additions & 14 deletions src/dotnetCampus.CommandLine/CommandLineAsyncHandlerBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
using System;
using System.Threading.Tasks;

using dotnetCampus.Cli.StateMachine;

using static dotnetCampus.Cli.Utils.CommandLineHelpers;
using static dotnetCampus.Cli.Utils.CommandLineRunner;

namespace dotnetCampus.Cli
{
Expand Down Expand Up @@ -34,16 +32,6 @@ internal CommandLineAsyncHandlerBuilder(CommandLine commandLine)
/// 2. 最多只会有一个谓词处理方法被执行,此方法会返回唯一那个处理方法的退出代码。
/// </remarks>
/// <returns>用于异步等待谓词处理方法退出代码的异步任务。</returns>
public Task<int> RunAsync()
{
var possibleVerb = FindPossibleVerb(CommandLine);
foreach (var exitCode in new HandleVerbStateMachine<Task<int>>(CommandLine.ToMatchList).Run(possibleVerb))
{
return exitCode;
}

ThrowIfVerbNotMatchedAsync(possibleVerb);
return Task.FromResult(0);
}
public Task<int> RunAsync() => RunCoreAsync(CommandLine);
}
}
127 changes: 127 additions & 0 deletions src/dotnetCampus.CommandLine/CommandLineFilters.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#pragma warning disable CA1303 // 请不要将文本作为本地化参数传递

using System;
using System.Diagnostics.Contracts;

using static dotnetCampus.Cli.Utils.CommandLineHelpers;

namespace dotnetCampus.Cli
{
/// <summary>
/// 包含命令行过滤器与拦截相关的扩展方法。
/// </summary>
public static class CommandLineFilters
{
/// <summary>
/// 添加一个命令行过滤器,用于在命令行执行前后处理和拦截一些通用的命令。
/// 与 AddHandler 不同的是,AddFilter 与顺序相关,先添加的 Filter 先执行。
/// </summary>
/// <typeparam name="TFilter">过滤器的类型。</typeparam>
/// <param name="builder">构造器模式。</param>
/// <returns>已收集的过滤器,可以继续收集或者开始执行。</returns>
[Pure]
public static CommandLineHandlerBuilder AddFilter<TFilter>(
this ICommandLineHandlerBuilder builder)
where TFilter : ICommandLineFilter, new()
=> AddFilterCore<TFilter>(
builder ?? throw new ArgumentNullException(nameof(builder)),
null);

/// <summary>
/// 添加一个命令行过滤器,用于在命令行执行前后处理和拦截一些通用的命令。
/// 与 AddHandler 不同的是,AddFilter 与顺序相关,先添加的 Filter 先执行。
/// </summary>
/// <typeparam name="TFilter">过滤器的类型。</typeparam>
/// <param name="builder">构造器模式。</param>
/// <param name="parser">命令行解析器。</param>
/// <returns>已收集的过滤器,可以继续收集或者开始执行。</returns>
[Pure]
public static CommandLineHandlerBuilder AddFilter<TFilter>(
this ICommandLineHandlerBuilder builder,
ICommandLineOptionParser<TFilter> parser)
where TFilter : ICommandLineFilter
=> AddFilterCore(
builder ?? throw new ArgumentNullException(nameof(builder)),
parser ?? throw new ArgumentNullException(nameof(parser)));

/// <summary>
/// 添加一个命令行过滤器,用于在命令行执行前后处理和拦截一些通用的命令。
/// 与 AddHandler 不同的是,AddFilter 与顺序相关,先添加的 Filter 先执行。
/// </summary>
/// <typeparam name="TFilter">过滤器的类型。</typeparam>
/// <param name="builder">构造器模式。</param>
/// <returns>已收集的过滤器,可以继续收集或者开始异步执行。</returns>
[Pure]
public static CommandLineAsyncHandlerBuilder AddFilter<TFilter>(
this ICommandLineAsyncHandlerBuilder builder)
where TFilter : ICommandLineFilter, new()
=> AddFilterCore<TFilter>(
builder ?? throw new ArgumentNullException(nameof(builder)),
null);

/// <summary>
/// 添加一个命令行过滤器,用于在命令行执行前后处理和拦截一些通用的命令。
/// 与 AddHandler 不同的是,AddFilter 与顺序相关,先添加的 Filter 先执行。
/// </summary>
/// <typeparam name="TFilter">过滤器的类型。</typeparam>
/// <param name="builder">构造器模式。</param>
/// <param name="parser">命令行解析器。</param>
/// <returns>已收集的过滤器,可以继续收集或者开始异步执行。</returns>
[Pure]
public static CommandLineAsyncHandlerBuilder AddFilter<TFilter>(
this ICommandLineAsyncHandlerBuilder builder,
ICommandLineOptionParser<TFilter>? parser = null)
where TFilter : ICommandLineFilter
=> AddFilterCore(
builder ?? throw new ArgumentNullException(nameof(builder)),
parser ?? throw new ArgumentNullException(nameof(parser)));

[Pure]
private static CommandLineHandlerBuilder AddFilterCore<TFilter>(
this ICommandLineHandlerBuilder builder,
ICommandLineOptionParser<TFilter>? parser)
where TFilter : ICommandLineFilter
{
if (builder is null)
{
throw new ArgumentNullException(nameof(builder));
}

AddFilterCore(builder.CommandLine, parser);

return builder switch
{
CommandLineHandlerBuilder commandLineBuilder => commandLineBuilder,
CommandLine commandLine => new CommandLineHandlerBuilder(commandLine),
_ => throw new NotSupportedException($"接口 {nameof(ICommandLineHandlerBuilder)} 不支持第三方实现。"),
};
}

[Pure]
private static CommandLineAsyncHandlerBuilder AddFilterCore<TFilter>(
this ICommandLineAsyncHandlerBuilder builder,
ICommandLineOptionParser<TFilter>? parser)
where TFilter : ICommandLineFilter
{
if (builder is null)
{
throw new ArgumentNullException(nameof(builder));
}

AddFilterCore(builder.CommandLine, parser);

return builder switch
{
CommandLineAsyncHandlerBuilder commandLineBuilder => commandLineBuilder,
_ => throw new NotSupportedException($"接口 {nameof(ICommandLineHandlerBuilder)} 不支持第三方实现。"),
};
}

[Pure]
private static void AddFilterCore<TFilter>(CommandLine commandLine, ICommandLineOptionParser<TFilter>? parser)
where TFilter : ICommandLineFilter
{
commandLine.AddMatch(MatchFilter(commandLine, parser));
}
}
}
18 changes: 2 additions & 16 deletions src/dotnetCampus.CommandLine/CommandLineHandlerBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

using dotnetCampus.Cli.StateMachine;

using static dotnetCampus.Cli.Utils.CommandLineHelpers;
using static dotnetCampus.Cli.Utils.CommandLineRunner;

namespace dotnetCampus.Cli
{
Expand Down Expand Up @@ -34,16 +30,6 @@ internal CommandLineHandlerBuilder(CommandLine commandLine)
/// 2. 最多只会有一个谓词处理方法被执行,此方法会返回唯一那个处理方法的退出代码。
/// </remarks>
/// <returns>谓词处理方法的退出代码。</returns>
public int Run()
{
var possibleVerb = FindPossibleVerb(CommandLine);
foreach (var exitCode in new HandleVerbStateMachine<Task<int>>(CommandLine.ToMatchList).Run(possibleVerb))
{
return exitCode.Result;
}

ThrowIfVerbNotMatchedAsync(possibleVerb);
return 0;
}
public int Run() => RunCoreAsync(CommandLine).Result;
}
}
Loading

0 comments on commit ab06c31

Please sign in to comment.