dotnet / dotnet/command-line-api
Post-Parsing / Type conversion validation / constraint API
- Dominant language
- C#
- Stars
- 3.7k
- Forks
- 428
- PR merge metrics
- No merged PRs in 30d
Description
_[[Moving to new issue per @jonsequitur [request](https://github.com/dotnet/command-line-api/issues/940#issuecomment-810502340)]]_
The current method to add constraints is too complex -- for example, the [current suggestions](https://github.com/dotnet/command-line-api/issues/940#issuecomment-647155920) require the user to **_re-parse the tokens_** and understand how to iterate though the internal data structures. Ouch.
Strawman goals / Scenarios (click to expand)
Goals
* [ ] Solution is invisible to clients which do not require the functionality
* [ ] Minimize code duplication
* [ ] Requires clients to writes minimal logic required for their particular use case
* [ ] Chainable / buildable ... user can add multiple constraints to an `Option`
Scenarios
* [ ] User wants to **_further restrict_** an already-supported type, validating individual values
* [ ] User wants to **_further restrict_** sets of values (for -arity > 1)
Strawman client code usage (click to expand)
OK, this uses really long names, solely to be more descriptive.
Framework-provided names would likely be much shorter.
```C#
new Option("-o")
.AddConstraintPerValue(IsPositiveValue)
.AddConstraintPerSet(ValuesAllEvenOrAllOdd);
new Option("-s")
.AddConstraintPerValue(StartsWithVowel)
.AddConstraintPerSet(AllStartWithSameLetter);
```
Strawman per-value constraints (click to expand)
The idea here is that, even if `Arity` maximum is greater than one, the library would take care of iterating.
```C#
public static bool IsPositiveValue(int value) { return value > 0; }
public static bool StartsWithVowel(string value) {
if (String.IsNullOrEmpty(value)) { return false; }
char tmp = Char.ToLowerInvariant(value[0]);
return (tmp == 'a' || tmp == 'e' || tmp == 'i' || tmp == 'o' || tmp == 'u' || tmp == 'y');
}
```
Strawman per-set-of-values constraints (click to expand)
The use of `IOrderedEnumerable` provides flexibility for the library to change the underlying data representation, while ensuring the order of the types matches what was passed via the command line.
The implementations are still simple, iterating through the provided set of values.
```C#
public static bool ValuesAllEvenOrAllOdd(IOrderedEnumerable values ) {
bool any = false;
bool areEven;
foreach (var v in values) {
if (!any) { areEven = ((v % 2) == 0); }
if (areEven && ((v % 2) != 0)) { return false; }
else if ((v % 2) == 0) { return false; }
}
// return any; // returns false for zero entries
return true; // returns true for zero entries
}
public static bool AllStartWithSameLetter(IOrderedEnumerable values) {
bool any = false;
char firstLetter;
foreach (var v in values) {
if (String.IsNullOrEmpty(v)) { return false; }
if (!any) { firstLetter = v[0]; }
if (v[0] != firstLetter) { return false; }
}
// return any; // returns false for zero entries
return true; // returns true for zero entries
}
```
I think this would be significantly improved experience for clients. Please consider making it simpler for the client code by enabling something such as the above?
Contributor guide
Assessment
This issue has not been assessed yet.