8000 some ergonomic improvements (#2559) · dotnet/command-line-api@bd9602c · GitHub
[go: up one dir, main page]

Skip to content

Commit bd9602c

Browse files
authored
some ergonomic improvements (#2559)
* a little project upkeep * make some validation methods fluent-friendly * fix some warnings * update API baseline
1 parent aba0e9a commit bd9602c

File tree

11 files changed

+140
-94
lines changed

11 files changed

+140
-94
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ _NCrunch_*/
147147
_ReSharper.*/
148148
buildlog.txt
149149
nCrunchTemp*
150+
.NCrunch_System.CommandLine/
150151
TestResults/
151152

152153
.fake

Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<PropertyGroup>
88
<NoWarn>$(NoWarn);NU5125;CS0618</NoWarn>
99
<PackageLicenseExpression>MIT</PackageLicenseExpression>
10-
<LangVersion>10.0</LangVersion>
10+
<LangVersion>13.0</LangVersion>
1111
<PackageProjectUrl>https://github.com/dotnet/command-line-api</PackageProjectUrl>
1212
</PropertyGroup>
1313

System.CommandLine.v3.ncrunchsolution

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
<Settings>
33
<AllowParallelTestExecution>True</AllowParallelTestExecution>
44
<CustomBuildProperties>
5-
<Value>TargetFrameworks = net7.0</Value>
6-
<Value>TargetFramework = net7.0</Value>
5+
<Value>TargetFrameworks = net8.0</Value>
6+
<Value>TargetFramework = net8.0</Value>
77
</CustomBuildProperties>
8+
<EnableRDI>False</EnableRDI>
9+
<RdiConfigured>True</RdiConfigured>
810
<SolutionConfigured>True</SolutionConfigured>
911
</Settings>
1012
</SolutionConfiguration>

core.slnf

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"solution": {
3+
"path": "System.CommandLine.sln",
4+
"projects": [
5+
"src\\System.CommandLine.ApiCompatibility.Tests\\System.CommandLine.ApiCompatibility.Tests.csproj",
6+
"src\\System.CommandLine.DragonFruit\\System.CommandLine.DragonFruit.csproj",
7+
"src\\System.CommandLine.NamingConventionBinder.Tests\\System.CommandLine.NamingConventionBinder.Tests.csproj",
8+
"src\\System.CommandLine.NamingConventionBinder\\System.CommandLine.NamingConventionBinder.csproj",
9+
"src\\System.CommandLine.Tests\\System.CommandLine.Tests.csproj",
10+
"src\\System.CommandLine\\System.CommandLine.csproj"
11+
]
12+
}
13+
}

src/System.CommandLine.ApiCompatibility.Tests/ApiCompatibilityApprovalTests.System_CommandLine_api_is_not_changed.approved.txt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,6 @@
1515
public Func<System.CommandLine.Parsing.ArgumentResult,T> DefaultValueFactory { get; set; }
1616
public System.Boolean HasDefaultValue { get; }
1717
public System.Type ValueType { get; }
18-
public System.Void AcceptLegalFileNamesOnly()
19-
public System.Void AcceptLegalFilePathsOnly()
20-
public System.Void AcceptOnlyFromAmong(System.String[] values)
2118
public struct ArgumentArity : System.ValueType, System.IEquatable<ArgumentArity>
2219
public static ArgumentArity ExactlyOne { get; }
2320
public static ArgumentArity OneOrMore { get; }
@@ -35,6 +32,9 @@
3532
public static Argument<System.IO.DirectoryInfo> AcceptExistingOnly(this Argument<System.IO.DirectoryInfo> argument)
3633
public static Argument<System.IO.FileSystemInfo> AcceptExistingOnly(this Argument<System.IO.FileSystemInfo> argument)
3734
public static Argument<T> AcceptExistingOnly<T>(this Argument<T> argument)
35+
public static Argument<T> AcceptLegalFileNamesOnly<T>(this Argument<T> argument)
36+
public static Argument<T> AcceptLegalFilePathsOnly<T>(this Argument<T> argument)
37+
public static Argument<T> AcceptOnlyFromAmong<T>(this Argument<T> argument, System.String[] values)
3838
public class Command : Symbol, System.Collections.IEnumerable
3939
.ctor(System.String name, System.String description = null)
4040
public System.CommandLine.Invocation.CommandLineAction Action { get; set; }
@@ -105,9 +105,9 @@
105105
public Func<System.CommandLine.Parsing.ArgumentResult,T> CustomParser { get; set; }
106106
public Func<System.CommandLine.Parsing.ArgumentResult,T> DefaultValueFactory { get; set; }
107107
public System.Type ValueType { get; }
108-
public System.Void AcceptLegalFileNamesOnly()
109-
public System.Void AcceptLegalFilePathsOnly()
110-
public System.Void AcceptOnlyFromAmong(System.String[] values)
108+
public Option<T> AcceptLegalFileNamesOnly()
109+
public Option<T> AcceptLegalFilePathsOnly()
110+
public Option<T> AcceptOnlyFromAmong(System.String[] values)
111111
public static class OptionValidation
112112
public static Option<System.IO.FileInfo> AcceptExistingOnly(this Option<System.IO.FileInfo> option)
113113
public static Option<System.IO.DirectoryInfo> AcceptExistingOnly(this Option<System.IO.DirectoryInfo> option)

src/System.CommandLine.NamingConventionBinder/BindingContextExtensions.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ private sealed class DummyStateHoldingHandler : BindingHandler
1717
public override Task<int> InvokeAsync(ParseResult parseResult, CancellationToken cancellationToken = default) => Task.FromResult(0);
1818
}
1919

20+
/// <summary>
21+
/// Gets the binding context for the specified parse result.
22+
/// </summary>
23+
/// <param name="parseResult">The parse result from the command line parsing.</param>
24+
/// <returns>The binding context associated with the parse result.</returns>
2025
public static BindingContext GetBindingContext(this ParseResult parseResult)
2126
{
2227
// parsing resulted with no handler or it was not created yet, we fake it to just store the BindingContext between the calls

src/System.CommandLine.NamingConventionBinder/BindingHandler.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66

77
namespace System.CommandLine.NamingConventionBinder
88
{
9+
/// <summary>
10+
/// Represents a handler that provides binding functionality for command-line actions.
11+
/// </summary>
12+
/// <remarks>This abstract class serves as a base for implementing custom binding logic in command-line
13+
/// applications. It provides a mechanism to retrieve or initialize a <see cref="BindingContext"/> for the current
14+
/// invocation.</remarks>
915
public abstract class BindingHandler : AsynchronousCommandLineAction
1016
{
1117
private BindingContext? _bindingContext;

src/System.CommandLine/ArgumentValidation.cs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,95 @@ public static Argument<T> AcceptExistingOnly<T>(this Argument<T> argument)
6969
return argument;
7070
}
7171

72+
/// <summary>
73+
/// Configures the argument to accept only values representing legal file names.
74+
/// </summary>
75+
/// <remarks>A parse error will result, for example, if file path separators are found in the parsed value.</remarks>
76+
public static Argument<T> AcceptLegalFileNamesOnly<T>(this Argument<T> argument)
77+
{
78+
argument.Validators.Add(static result =>
79+
{
80+
var invalidFileNameChars = Path.GetInvalidFileNameChars();
81+
82+
for (var i = 0; i < result.Tokens.Count; i++)
83+
{
84+
var token = result.Tokens[i];
85+
var invalidCharactersIndex = token.Value.IndexOfAny(invalidFileNameChars);
86+
87+
if (invalidCharactersIndex >= 0)
88+
{
89+
result.AddError(LocalizationResources.InvalidCharactersInFileName(token.Value[invalidCharactersIndex]));
90+
}
91+
}
92+
});
93+
94< F42D /td>+
return argument;
95+
}
96+
97+
98+
/// <summary>
99+
/// Configures the argument to accept only values representing legal file paths.
100+
/// </summary>
101+
public static Argument<T> AcceptLegalFilePathsOnly<T>(this Argument<T> argument)
102+
{
103+
argument.Validators.Add(static result =>
104+
{
105+
var invalidPathChars = Path.GetInvalidPathChars();
106+
107+
for (var i = 0; i < result.Tokens.Count; i++)
108+
{
109+
var token = result.Tokens[i];
110+
111+
// File class no longer check invalid character
112+
// https://blogs.msdn.microsoft.com/jeremykuhne/2018/03/09/custom-directory-enumeration-in-net-core-2-1/
113+
var invalidCharactersIndex = token.Value.IndexOfAny(invalidPathChars);
114+
115+
if (invalidCharactersIndex >= 0)
116+
{
117+
result.AddError(LocalizationResources.InvalidCharactersInPath(token.Value[invalidCharactersIndex]));
118+
}
119+
}
120+
});
121+
122+
return argument;
123+
}
124+
125+
/// <summary>
126+
/// Configures the argument to accept only the specified values, and to suggest them as command line completions.
127+
/// </summary>
128+
/// <param name="argument">The argument to configure.</param>
129+
/// <param name="values">The values that are allowed for the argument.</param>
130+
public static Argument<T> AcceptOnlyFromAmong<T>(
131+
this Argument<T> argument,
132+
params string[] values)
133+
{
134+
if (values is not null && values.Length > 0)
135+
{
136+
argument.Validators.Clear();
137+
argument.Validators.Add(UnrecognizedArgumentError);
138+
argument.CompletionSources.Clear();
139+
argument.CompletionSources.Add(values);
140+
}
141+
142+
return argument;
143+
144+
void UnrecognizedArgumentError(ArgumentResult argumentResult)
145+
{
146+
for (var i = 0; i < argumentResult.Tokens.Count; i++)
147+
{
148+
var token = argumentResult.Tokens[i];
149+
150+
if (token.Symbol is null || token.Symbol == argument)
151+
{
152+
if (Array.IndexOf(values, token.Value) < 0)
153+
{
154+
argumentResult.AddError(LocalizationResources.UnrecognizedArgument(token.Value, values));
155+
}
156+
}
157+
}
158+
}
159+
}
160+
72161
private static void FileOrDirectoryExists<T>(ArgumentResult result)
73162
where T : FileSystemInfo
74163
{

src/System.CommandLine/Argument{T}.cs

Lines changed: 0 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
using System.Collections.Generic;
55
using System.CommandLine.Parsing;
66
using System.Diagnostics.CodeAnalysis;
7-
using System.IO;
8-
97
namespace System.CommandLine
108
{
119
/// <inheritdoc cref="Argument" />
@@ -84,85 +82,6 @@ public Argument(string name) : base(name)
8482
return DefaultValueFactory.Invoke(argumentResult);
8583
}
8684

87-
/// <summary>
88-
/// Configures the argument to accept only the specified values, and to suggest them as command line completions.
89-
/// </summary>
90-
/// <param name="values">The values that are allowed for the argument.</param>
91-
public void AcceptOnlyFromAmong(params string[] values)
92-
{
93-
if (values is not null && values.Length > 0)
94-
{
95-
Validators.Clear();
96-
Validators.Add(UnrecognizedArgumentError);
97-
CompletionSources.Clear();
98-
CompletionSources.Add(values);
99-
}
100-
101-
void UnrecognizedArgumentError(ArgumentResult argumentResult)
102-
{
103-
for (var i = 0; i < argumentResult.Tokens.Count; i++)
104-
{
105-
var token = argumentResult.Tokens[i];
106-
107-
if (token.Symbol is null || token.Symbol == this)
108-
{
109-
if (Array.IndexOf(values, token.Value) < 0)
110-
{
111-
argumentResult.AddError(LocalizationResources.UnrecognizedArgument(token.Value, values));
112-
}
113-
}
114-
}
115-
}
116-
}
117-
118-
/// <summary>
119-
/// Configures the argument to accept only values representing legal file paths.
120-
/// </summary>
121-
public void AcceptLegalFilePathsOnly()
122-
{
123-
Validators.Add(static result =>
124-
{
125-
var invalidPathChars = Path.GetInvalidPathChars();
126-
127-
for (var i = 0; i < result.Tokens.Count; i++)
128-
{
129-
var token = result.Tokens[i];
130-
131-
// File class no longer check invalid character
132-
// https://blogs.msdn.microsoft.com/jeremykuhne/2018/03/09/custom-directory-enumeration-in-net-core-2-1/
133-
var invalidCharactersIndex = token.Value.IndexOfAny(invalidPathChars);
134-
135-
if (invalidCharactersIndex >= 0)
136-
{
137-
result.AddError(LocalizationResources.InvalidCharactersInPath(token.Value[invalidCharactersIndex]));
138-
}
139-
}
140-
});
141-
}
142-
143-
/// <summary>
144-
/// Configures the argument to accept only values representing legal file names.
145-
/// </summary>
146-
/// <remarks>A parse error will result, for example, if file path separators are found in the parsed value.</remarks>
147-
public void AcceptLegalFileNamesOnly()
148-
{
149-
Validators.Add(static result =>
150-
{
151-
var invalidFileNameChars = Path.GetInvalidFileNameChars();
152-
153-
for (var i = 0; i < result.Tokens.Count; i++)
154-
{
155-
var token = result.Tokens[i];
156-
var invalidCharactersIndex = token.Value.IndexOfAny(invalidFileNameChars);
157-
158-
if (invalidCharactersIndex >= 0)
159-
{
160-
result.AddError(LocalizationResources.InvalidCharactersInFileName(token.Value[invalidCharactersIndex]));
161-
}
162-
}
163-
});
164-
}
165-
16685
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL3050", Justification = "https://github.com/dotnet/command-line-api/issues/1638")]
16786
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2091", Justification = "https://github.com/dotnet/command-line-api/issues/1638")]
16887
internal static T? CreateDefaultValue()

src/System.CommandLine/OptionValidation.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ public static Option<T> AcceptExistingOnly<T>(this Option<T> option)
5454
where T : IEnumerable<FileSystemInfo>
5555
{
5656
option._argument.AcceptExistingOnly();
57-
5857
return option;
5958
}
6059
}

0 commit comments

Comments
 (0)
0