Skip to content

Commit b5e7d05

Browse files
committed
Strongly type SetRange, add GetRange
1 parent 71f3ca4 commit b5e7d05

File tree

6 files changed

+55
-29
lines changed

6 files changed

+55
-29
lines changed

src/System.CommandLine.Subsystems.Tests/ValidationSubsystemTests.cs

+15-15
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,18 @@ namespace System.CommandLine.Subsystems.Tests;
1111
public class ValidationSubsystemTests
1212
{
1313
// Running exactly the same code is important here because missing a step will result in a false positive. Ask me how I know
14-
private CliOption GetOptionWithSimpleRange<T>(T lowerBound, T upperBound)
14+
private CliOption<T> GetOptionWithSimpleRange<T>(string name, T lowerBound, T upperBound)
1515
where T : IComparable<T>
1616
{
17-
var option = new CliOption<int>("--intOpt");
17+
var option = new CliOption<T>(name);
1818
option.SetRange(lowerBound, upperBound);
1919
return option;
2020
}
2121

22-
private CliOption GetOptionWithRangeBounds<T>(ValueSource<T> lowerBound, ValueSource<T> upperBound)
22+
private CliOption<T> GetOptionWithRangeBounds<T>(string name, ValueSource<T> lowerBound, ValueSource<T> upperBound)
2323
where T : IComparable<T>
2424
{
25-
var option = new CliOption<int>("--intOpt");
25+
var option = new CliOption<T>(name);
2626
option.SetRange(lowerBound, upperBound);
2727
return option;
2828
}
@@ -45,7 +45,7 @@ private PipelineResult ExecutedPipelineResultForCommand(CliCommand command, stri
4545
[Fact]
4646
public void Int_values_in_specified_range_do_not_have_errors()
4747
{
48-
var option = GetOptionWithSimpleRange(0, 50);
48+
var option = GetOptionWithSimpleRange("--intOpt", 0, 50);
4949

5050
var pipelineResult = ExecutedPipelineResultForRangeOption(option, "--intOpt 42");
5151

@@ -56,7 +56,7 @@ public void Int_values_in_specified_range_do_not_have_errors()
5656
[Fact]
5757
public void Int_values_above_upper_bound_report_error()
5858
{
59-
var option = GetOptionWithSimpleRange(0, 5);
59+
var option = GetOptionWithSimpleRange("--intOpt", 0, 5);
6060

6161
var pipelineResult = ExecutedPipelineResultForRangeOption(option, "--intOpt 42");
6262

@@ -69,7 +69,7 @@ public void Int_values_above_upper_bound_report_error()
6969
[Fact]
7070
public void Int_below_lower_bound_report_error()
7171
{
72-
var option = GetOptionWithSimpleRange(0, 5);
72+
var option = GetOptionWithSimpleRange("--intOpt", 0, 5);
7373

7474
var pipelineResult = ExecutedPipelineResultForRangeOption(option, "--intOpt -42");
7575

@@ -82,7 +82,7 @@ public void Int_below_lower_bound_report_error()
8282
[Fact]
8383
public void Int_values_on_lower_range_bound_do_not_report_error()
8484
{
85-
var option = GetOptionWithSimpleRange(42, 50);
85+
var option = GetOptionWithSimpleRange("--intOpt", 42, 50);
8686

8787
var pipelineResult = ExecutedPipelineResultForRangeOption(option, "--intOpt 42");
8888

@@ -93,7 +93,7 @@ public void Int_values_on_lower_range_bound_do_not_report_error()
9393
[Fact]
9494
public void Int_values_on_upper_range_bound_do_not_report_error()
9595
{
96-
var option = GetOptionWithSimpleRange(0, 42);
96+
var option = GetOptionWithSimpleRange("--intOpt", 0, 42);
9797

9898
var pipelineResult = ExecutedPipelineResultForRangeOption(option, "--intOpt 42");
9999

@@ -104,7 +104,7 @@ public void Int_values_on_upper_range_bound_do_not_report_error()
104104
[Fact]
105105
public void Values_below_calculated_lower_bound_report_error()
106106
{
107-
var option = GetOptionWithRangeBounds<int>(ValueSource.Create(() => (true, 1)), 50);
107+
var option = GetOptionWithRangeBounds<int>("--intOpt", ValueSource.Create(() => (true, 1)), 50);
108108

109109
var pipelineResult = ExecutedPipelineResultForRangeOption(option, "--intOpt 0");
110110

@@ -118,7 +118,7 @@ public void Values_below_calculated_lower_bound_report_error()
118118
[Fact]
119119
public void Values_within_calculated_range_do_not_report_error()
120120
{
121-
var option = GetOptionWithRangeBounds(ValueSource<int>.Create(() => (true, 1)), ValueSource<int>.Create(() => (true, 50)));
121+
var option = GetOptionWithRangeBounds("--intOpt", ValueSource.Create(() => (true, 1)), ValueSource.Create(() => (true, 50)));
122122

123123
var pipelineResult = ExecutedPipelineResultForRangeOption(option, "--intOpt 42");
124124

@@ -129,7 +129,7 @@ public void Values_within_calculated_range_do_not_report_error()
129129
[Fact]
130130
public void Values_above_calculated_upper_bound_report_error()
131131
{
132-
var option = GetOptionWithRangeBounds(0, ValueSource<int>.Create(() => (true, 40)));
132+
var option = GetOptionWithRangeBounds("--intOpt", 0, ValueSource.Create(() => (true, 40)));
133133

134134
var pipelineResult = ExecutedPipelineResultForRangeOption(option, "--intOpt 42");
135135

@@ -143,7 +143,7 @@ public void Values_above_calculated_upper_bound_report_error()
143143
public void Values_below_relative_lower_bound_report_error()
144144
{
145145
var otherOption = new CliOption<int>("-a");
146-
var option = GetOptionWithRangeBounds(ValueSource.Create(otherOption, o => (true, (int)o + 1)), 50);
146+
var option = GetOptionWithRangeBounds("--intOpt", ValueSource.Create(otherOption, o => (true, (int)o + 1)), 50);
147147
var command = new CliCommand("cmd") { option, otherOption };
148148

149149
var pipelineResult = ExecutedPipelineResultForCommand(command, "--intOpt 0 -a 0");
@@ -159,7 +159,7 @@ public void Values_below_relative_lower_bound_report_error()
159159
public void Values_within_relative_range_do_not_report_error()
160160
{
161161
var otherOption = new CliOption<int>("-a");
162-
var option = GetOptionWithRangeBounds(ValueSource<int>.Create(otherOption, o => (true, (int)o + 1)), ValueSource<int>.Create(otherOption, o => (true, (int)o + 10)));
162+
var option = GetOptionWithRangeBounds("--intOpt", ValueSource.Create(otherOption, o => (true, (int)o + 1)), ValueSource.Create(otherOption, o => (true, (int)o + 10)));
163163
var command = new CliCommand("cmd") { option, otherOption };
164164

165165
var pipelineResult = ExecutedPipelineResultForCommand(command, "--intOpt 11 -a 3");
@@ -172,7 +172,7 @@ public void Values_within_relative_range_do_not_report_error()
172172
public void Values_above_relative_upper_bound_report_error()
173173
{
174174
var otherOption = new CliOption<int>("-a");
175-
var option = GetOptionWithRangeBounds(0, ValueSource<int>.Create(otherOption, o => (true, (int)o + 10)));
175+
var option = GetOptionWithRangeBounds("--intOpt", 0, ValueSource.Create(otherOption, o => (true, (int)o + 10)));
176176
var command = new CliCommand("cmd") { option, otherOption };
177177

178178
var pipelineResult = ExecutedPipelineResultForCommand(command, "--intOpt 9 -a -2");

src/System.CommandLine.Subsystems/ValueConditionAnnotationExtensions.cs

+22-11
Original file line numberDiff line numberDiff line change
@@ -13,41 +13,52 @@ namespace System.CommandLine;
1313
public static class ValueConditionAnnotationExtensions
1414
{
1515
/// <summary>
16-
/// Set the upper and/or lower bound values of the range.
16+
/// Set upper and/or lower bounds on the range of values that the symbol value may have.
1717
/// </summary>
18-
/// <typeparam name="T">The type of the bounds.</typeparam>
18+
/// <typeparam name="TValueSymbol">The type of the symbol whose value is bounded by the range.</typeparam>
19+
/// <typeparam name="TValue">The type of the value that is bounded by the range.</typeparam>
1920
/// <param name="symbol">The option or argument the range applies to.</param>
2021
/// <param name="lowerBound">The lower bound of the range.</param>
2122
/// <param name="upperBound">The upper bound of the range.</param>
2223
// TODO: Add RangeBounds
2324
// TODO: You should not have to set both...why not nullable?
24-
public static void SetRange<T>(this CliValueSymbol symbol, T lowerBound, T upperBound)
25-
where T : IComparable<T>
25+
public static void SetRange<TValueSymbol, TValue>(this TValueSymbol symbol, TValue lowerBound, TValue upperBound)
26+
where TValueSymbol : CliValueSymbol, ICliValueSymbol<TValue>
27+
where TValue : IComparable<TValue>
2628
{
27-
var range = new Range<T>(lowerBound, upperBound);
29+
var range = new Range<TValue>(lowerBound, upperBound);
2830

2931
symbol.SetValueCondition(range);
3032
}
3133

3234
/// <summary>
33-
/// Set the upper and/or lower bound via ValueSource. Implicit conversions means this
34-
/// generally just works with any <see cref="ValueSource">.
35+
/// Set upper and/or lower <see cref="ValueSource"> bounds on the range of values that the symbol value may have.
36+
/// Implicit conversions means this generally just works with any <see cref="ValueSource">.
3537
/// </summary>
36-
/// <typeparam name="T">The type of the bounds.</typeparam>
38+
/// <typeparam name="TValueSymbol">The type of the symbol whose value is bounded by the range.</typeparam>
39+
/// <typeparam name="TValue">The type of the value that is bounded by the range.</typeparam>
3740
/// <param name="symbol">The option or argument the range applies to.</param>
3841
/// <param name="lowerBound">The <see cref="ValueSource"> that is the lower bound of the range.</param>
3942
/// <param name="upperBound">The <see cref="ValueSource"> that is the upper bound of the range.</param>
4043
// TODO: Add RangeBounds
4144
// TODO: You should not have to set both...why not nullable?
42-
public static void SetRange<T>(this CliValueSymbol symbol, ValueSource<T> lowerBound, ValueSource<T> upperBound)
43-
where T : IComparable<T>
45+
public static void SetRange<TValueSymbol, TValue>(this TValueSymbol symbol, ValueSource<TValue> lowerBound, ValueSource<TValue> upperBound)
46+
where TValueSymbol : CliValueSymbol, ICliValueSymbol<TValue>
47+
where TValue : IComparable<TValue>
4448
// TODO: You should not have to set both...why not nullable?
4549
{
46-
var range = new Range<T>(lowerBound, upperBound);
50+
var range = new Range<TValue>(lowerBound, upperBound);
4751

4852
symbol.SetValueCondition(range);
4953
}
5054

55+
/// <summary>
56+
/// Get the upper and/or lower bound of the symbol's value.
57+
/// </summary>
58+
/// <param name="symbol">The option or argument the range applies to.</param>
59+
public static ValueConditions.Range? GetRange(this CliValueSymbol symbol)
60+
=> symbol.GetValueCondition<ValueConditions.Range>();
61+
5162
/// <summary>
5263
/// Indicates that there is an inclusive group of options and arguments for the command. All
5364
/// members of an inclusive must be present, or none can be present.

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
using System.Collections.Generic;
55
using System.CommandLine.Parsing;
66
using System.Diagnostics.CodeAnalysis;
7-
using System.IO;
87

98
namespace System.CommandLine
109
{
10+
1111
/// <inheritdoc cref="CliArgument" />
12-
public class CliArgument<T> : CliArgument
12+
public class CliArgument<T> : CliArgument, ICliValueSymbol<T>
1313
{
1414
// TODO: custom parser
1515
/*

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace System.CommandLine
77
{
88
/// <inheritdoc cref="CliOption" />
99
/// <typeparam name="T">The <see cref="System.Type"/> that the option's arguments are expected to be parsed as.</typeparam>
10-
public class CliOption<T> : CliOption
10+
public class CliOption<T> : CliOption, ICliValueSymbol<T>
1111
{
1212
// TODO: do not expose private fields
1313
internal readonly CliArgument<T> _argument;
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright (c) .NET Foundation and contributors. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
namespace System.CommandLine;
5+
6+
/// <summary>
7+
/// This is applied to <see cref="CliArgument{T}"/> and <see cref="CliArgument{T}"/>, and allows
8+
/// allows methods with a <see cref="CliValueSymbol"/> argument to apply constraints based on the
9+
/// value type.
10+
/// </summary>
11+
/// <typeparam name="TValue">The value type</typeparam>
12+
public interface ICliValueSymbol<TValue>
13+
{
14+
}

src/System.CommandLine/System.CommandLine.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
<Compile Include="ArgumentArity.cs" />
2828
<Compile Include="Binding\ArgumentConversionResult.cs" />
2929
<Compile Include="CliValueSymbol.cs" />
30+
<Compile Include="ICliValueSymbol.cs" />
3031
<Compile Include="Parsing\CliCommandResult.cs" />
3132
<Compile Include="Parsing\CliSymbolResult.cs" />
3233
<Compile Include="Parsing\SymbolLookupByName.cs" />

0 commit comments

Comments
 (0)