Skip to content

Commit b1cb1f8

Browse files
authored
Merge pull request #46 from dotnet-campus/t/walterlv/parent-child
支持带有父子类关系的命令行数据模型
2 parents 360594a + 3471a0b commit b1cb1f8

File tree

8 files changed

+569
-8
lines changed

8 files changed

+569
-8
lines changed

src/DotNetCampus.CommandLine.Analyzer/Generators/ModelProviding/CommandModelProvider.cs

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,16 +50,26 @@ public static IncrementalValuesProvider<CommandOptionsGeneratingModel> SelectCom
5050
var attribute = typeSymbol.GetAttributes()
5151
.FirstOrDefault(a => a.AttributeClass!.IsAttributeOf<VerbAttribute>());
5252
// 4. 拥有 [Option] 特性的属性。
53-
var optionProperties = typeSymbol.GetMembers()
54-
.OfType<IPropertySymbol>()
55-
.Select(OptionPropertyGeneratingModel.TryParse)
53+
var optionProperties = typeSymbol
54+
.EnumerateBaseTypesRecursively() // 递归获取所有基类
55+
.Reverse() // (注意我们先给父类属性赋值,再给子类属性赋值)
56+
.SelectMany(x => x.GetMembers()) // 的所有成员,
57+
.OfType<IPropertySymbol>() // 然后取出属性,
58+
.Select(OptionPropertyGeneratingModel.TryParse) // 解析出 OptionPropertyGeneratingModel。
5659
.OfType<OptionPropertyGeneratingModel>()
60+
.GroupBy(x => x.PropertyName) // 按属性名去重。
61+
.Select(x => x.Last()) // 随后,取子类的属性(去除父类的重名属性)。
5762
.ToImmutableArray();
5863
// 5. 拥有 [Value] 特性的属性。
59-
var valueProperties = typeSymbol.GetMembers()
60-
.OfType<IPropertySymbol>()
61-
.Select(ValuePropertyGeneratingModel.TryParse)
64+
var valueProperties = typeSymbol
65+
.EnumerateBaseTypesRecursively() // 递归获取所有基类
66+
.Reverse() // (注意我们先给父类属性赋值,再给子类属性赋值)
67+
.SelectMany(x => x.GetMembers()) // 的所有成员,
68+
.OfType<IPropertySymbol>() // 然后取出属性,
69+
.Select(ValuePropertyGeneratingModel.TryParse) // 解析出 ValuePropertyGeneratingModel。
6270
.OfType<ValuePropertyGeneratingModel>()
71+
.GroupBy(x => x.PropertyName) // 按属性名去重。
72+
.Select(x => x.Last()) // 随后,取子类的属性(去除父类的重名属性)。
6373
.ToImmutableArray();
6474

6575
if (!isOptions && !isHandler && attribute is null && optionProperties.IsEmpty && valueProperties.IsEmpty)
@@ -304,3 +314,16 @@ internal record AssemblyCommandsGeneratingModel
304314

305315
public required INamedTypeSymbol AssemblyCommandHandlerType { get; init; }
306316
}
317+
318+
file static class Extensions
319+
{
320+
public static IEnumerable<ITypeSymbol> EnumerateBaseTypesRecursively(this ITypeSymbol type)
321+
{
322+
var current = type;
323+
while (current != null)
324+
{
325+
yield return current;
326+
current = current.BaseType;
327+
}
328+
}
329+
}

tests/DotNetCampus.CommandLine.Performance/CommandLineParserTest.cs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.IO;
33
using BenchmarkDotNet.Attributes;
44
using CommandLine;
5+
using dotnetCampus.Cli;
56
using DotNetCampus.Cli.Performance.Fakes;
67
using DotNetCampus.Cli.Tests.Fakes;
78
using static DotNetCampus.Cli.Tests.Fakes.CommandLineArgs;
@@ -51,6 +52,20 @@ public void Parse_NoArgs_PowerShell()
5152
commandLine.As<Options>();
5253
}
5354

55+
[Benchmark(Description = "parse [] -v=3.x -p=parser")]
56+
public void Parse_NoArgs_3x_Parser()
57+
{
58+
var commandLine = dotnetCampus.Cli.CommandLine.Parse(NoArgs);
59+
commandLine.As(new OptionsParser());
60+
}
61+
62+
[Benchmark(Description = "parse [] -v=3.x -p=runtime")]
63+
public void Parse_NoArgs_3x_Runtime()
64+
{
65+
var commandLine = dotnetCampus.Cli.CommandLine.Parse(NoArgs);
66+
commandLine.As<Options>();
67+
}
68+
5469
[Benchmark(Description = "parse [PS1] --flexible")]
5570
public void Parse_PowerShell_Flexible()
5671
{
@@ -65,6 +80,20 @@ public void Parse_PowerShell_PowerShell()
6580
commandLine.As<Options>();
6681
}
6782

83+
[Benchmark(Description = "parse [PS1] -v=3.x -p=parser")]
84+
public void Parse_PowerShell_3x_Parser()
85+
{
86+
var commandLine = dotnetCampus.Cli.CommandLine.Parse(WindowsStyleArgs);
87+
commandLine.As(new OptionsParser());
88+
}
89+
90+
[Benchmark(Description = "parse [PS1] -v=3.x -p=runtime")]
91+
public void Parse_PowerShell_3x_Runtime()
92+
{
93+
var commandLine = dotnetCampus.Cli.CommandLine.Parse(WindowsStyleArgs);
94+
commandLine.As<Options>();
95+
}
96+
6897
[Benchmark(Description = "parse [CMD] --flexible")]
6998
public void Parse_Cmd_Flexible()
7099
{
@@ -79,6 +108,20 @@ public void Parse_Cmd_PowerShell()
79108
commandLine.As<Options>();
80109
}
81110

111+
[Benchmark(Description = "parse [CMD] -v=3.x -p=parser")]
112+
public void Parse_Cmd_3x_Parser()
113+
{
114+
var commandLine = dotnetCampus.Cli.CommandLine.Parse(CmdStyleArgs);
115+
commandLine.As(new OptionsParser());
116+
}
117+
118+
[Benchmark(Description = "parse [CMD] -v=3.x -p=runtime")]
119+
public void Parse_Cmd_3x_Runtime()
120+
{
121+
var commandLine = dotnetCampus.Cli.CommandLine.Parse(CmdStyleArgs);
122+
commandLine.As<Options>();
123+
}
124+
82125
[Benchmark(Description = "parse [GNU] --flexible")]
83126
public void Parse_Gnu_Flexible()
84127
{
@@ -93,6 +136,20 @@ public void Parse_Gnu_Gnu()
93136
commandLine.As<Options>();
94137
}
95138

139+
[Benchmark(Description = "parse [GNU] -v=3.x -p=parser")]
140+
public void Parse_Gnu_3x_Parser()
141+
{
142+
var commandLine = dotnetCampus.Cli.CommandLine.Parse(LinuxStyleArgs);
143+
commandLine.As(new OptionsParser());
144+
}
145+
146+
[Benchmark(Description = "parse [GNU] -v=3.x -p=runtime")]
147+
public void Parse_Gnu_3x_Runtime()
148+
{
149+
var commandLine = dotnetCampus.Cli.CommandLine.Parse(LinuxStyleArgs);
150+
commandLine.As<Options>();
151+
}
152+
96153
[Benchmark(Description = "handle [Edit,Print] --flexible")]
97154
public void Handle_Verbs_Flexible()
98155
{
@@ -102,13 +159,37 @@ public void Handle_Verbs_Flexible()
102159
.Run();
103160
}
104161

162+
[Benchmark(Description = "handle [Edit,Print] -v=3.x -p=runtime")]
163+
public void Handle_Verbs_Runtime()
164+
{
165+
var commandLine = dotnetCampus.Cli.CommandLine.Parse(EditVerbArgs);
166+
commandLine
167+
.AddHandler<EditOptions>(options => 0)
168+
.AddHandler<PrintOptions>(options => 0)
169+
.Run();
170+
}
171+
105172
[Benchmark(Description = "parse [URL]")]
106173
public void Parse_Url()
107174
{
108175
var commandLine = CommandLine.Parse(UrlArgs, new CommandLineParsingOptions { SchemeNames = ["walterlv"] });
109176
commandLine.As<Options>();
110177
}
111178

179+
[Benchmark(Description = "parse [URL] -v=3.x -p=parser")]
180+
public void Parse_Url_3x_Parser()
181+
{
182+
var commandLine = dotnetCampus.Cli.CommandLine.Parse(UrlArgs);
183+
commandLine.As(new OptionsParser());
184+
}
185+
186+
[Benchmark(Description = "parse [URL] -v=3.x -p=runtime")]
187+
public void Parse_Url_3x_Runtime()
188+
{
189+
var commandLine = dotnetCampus.Cli.CommandLine.Parse(UrlArgs);
190+
commandLine.As<Options>();
191+
}
192+
112193
[Benchmark(Description = "NuGet: CommandLineParser")]
113194
public void CommandLineParser()
114195
{

tests/DotNetCampus.CommandLine.Performance/DotNetCampus.CommandLine.Performance.csproj

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,17 @@
1414
<ItemGroup>
1515
<Compile Include="..\DotNetCampus.CommandLine.Tests\Fakes\**" LinkBase="Fakes" />
1616
<Compile Remove="..\DotNetCampus.CommandLine.Tests\Fakes\**\Runtime*.cs" />
17-
<Compile Remove="..\DotNetCampus.CommandLine.Tests\Fakes\**\*Parser.cs" />
1817
</ItemGroup>
1918

2019
<ItemGroup>
2120
<ProjectReference Include="..\..\src\DotNetCampus.CommandLine.Analyzer\DotNetCampus.CommandLine.Analyzer.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
2221
<ProjectReference Include="..\..\src\DotNetCampus.CommandLine\DotNetCampus.CommandLine.csproj" />
2322
</ItemGroup>
2423

24+
<ItemGroup>
25+
<Reference Include="dotnetCampus.CommandLine.Legacy">
26+
<HintPath>dotnetCampus.CommandLine.Legacy.dll</HintPath>
27+
</Reference>
28+
</ItemGroup>
29+
2530
</Project>
Binary file not shown.

tests/DotNetCampus.CommandLine.Tests/Fakes/AmbiguousOptionsParser.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Linq;
55
using System.Text;
66
using System.Threading.Tasks;
7+
using dotnetCampus.Cli;
78

89
namespace DotNetCampus.Cli.Tests.Fakes
910
{

tests/DotNetCampus.CommandLine.Tests/Fakes/Options.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,32 @@ public class Options
4949
/// </summary>
5050
[Option("StartupSession")]
5151
public string? StartupSession { get; init; }
52+
53+
/// <summary>
54+
/// 创建 <see cref="Options"/> 类的新实例。
55+
/// </summary>
56+
public Options()
57+
{
58+
}
59+
60+
/// <summary>
61+
/// 创建 <see cref="Options"/> 类的新实例。
62+
/// </summary>
63+
public Options(
64+
string? filePath,
65+
bool isFromCloud,
66+
string? startupMode,
67+
bool isSilence,
68+
bool isIwb,
69+
string? placement,
70+
string? startupSession)
71+
{
72+
FilePath = filePath;
73+
IsFromCloud = isFromCloud;
74+
StartupMode = startupMode;
75+
IsSilence = isSilence;
76+
IsIwb = isIwb;
77+
Placement = placement;
78+
StartupSession = startupSession;
79+
}
5280
}

tests/DotNetCampus.CommandLine.Tests/Fakes/OptionsParser.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Collections.Generic;
2+
using dotnetCampus.Cli;
23

34
namespace DotNetCampus.Cli.Tests.Fakes
45
{
@@ -93,4 +94,4 @@ public Options Commit()
9394
return new Options(_filePath, _isFromCloud, _startupMode, _isSilence, _isIwb, _placement, _startupSession);
9495
}
9596
}
96-
}
97+
}

0 commit comments

Comments
 (0)