diff --git a/Core/NetArgumentParser/Attributes/MultipleValueOptionAttribute.cs b/Core/NetArgumentParser/Attributes/MultipleValueOptionAttribute.cs
index fce7283..b8e2e1b 100644
--- a/Core/NetArgumentParser/Attributes/MultipleValueOptionAttribute.cs
+++ b/Core/NetArgumentParser/Attributes/MultipleValueOptionAttribute.cs
@@ -147,7 +147,7 @@ public override ICommonOption CreateOption(object source, PropertyInfo propertyI
private static OptionValueRestriction>? CreateValueRestriction(string? data)
{
return data is not null && !string.IsNullOrWhiteSpace(data)
- ? OptionValueRestrictionParser.ParseForList(data, true)
+ ? new OptionValueRestrictionParser().ParseForList(data, true)
: null;
}
}
diff --git a/Core/NetArgumentParser/Attributes/ValueOptionAttribute.cs b/Core/NetArgumentParser/Attributes/ValueOptionAttribute.cs
index 9c9816e..c48413d 100644
--- a/Core/NetArgumentParser/Attributes/ValueOptionAttribute.cs
+++ b/Core/NetArgumentParser/Attributes/ValueOptionAttribute.cs
@@ -172,7 +172,7 @@ public override ICommonOption CreateOption(object source, PropertyInfo propertyI
private static OptionValueRestriction? CreateValueRestriction(string? data)
{
return data is not null && !string.IsNullOrWhiteSpace(data)
- ? OptionValueRestrictionParser.Parse(data, true)
+ ? new OptionValueRestrictionParser().Parse(data, true)
: null;
}
diff --git a/Core/NetArgumentParser/NetArgumentParser.csproj b/Core/NetArgumentParser/NetArgumentParser.csproj
index ddc0010..9897e8d 100644
--- a/Core/NetArgumentParser/NetArgumentParser.csproj
+++ b/Core/NetArgumentParser/NetArgumentParser.csproj
@@ -2,7 +2,7 @@
NetArgumentParser
- 1.0.6
+ 1.0.7
NetArgumentParser
NetArgumentParser
yakovypg
diff --git a/Core/NetArgumentParser/Options/Configuration/OptionValueRestrictionParser.cs b/Core/NetArgumentParser/Options/Configuration/OptionValueRestrictionParser.cs
index 4198cb4..eaed4f0 100644
--- a/Core/NetArgumentParser/Options/Configuration/OptionValueRestrictionParser.cs
+++ b/Core/NetArgumentParser/Options/Configuration/OptionValueRestrictionParser.cs
@@ -8,12 +8,21 @@
namespace NetArgumentParser.Options.Configuration;
-public static class OptionValueRestrictionParser
+public class OptionValueRestrictionParser
{
public const string ExpectedFormat = "f1 p1 ...\\nOP f2 p1 ...\\n ...\\n?msg";
- public const char PartsSeparator = '\n';
- public const char SubpartsSeparator = ' ';
- public const char MessageIndicator = '?';
+
+ public OptionValueRestrictionParser(
+ char partsSeparator = '\n',
+ char subpartsSeparator = ' ',
+ char negationIndicator = '!',
+ char messageIndicator = '?')
+ {
+ PartsSeparator = partsSeparator;
+ SubpartsSeparator = subpartsSeparator;
+ NegationIndicator = negationIndicator;
+ MessageIndicator = messageIndicator;
+ }
private enum LogicalOperator
{
@@ -21,7 +30,12 @@ private enum LogicalOperator
Or
}
- public static OptionValueRestriction Parse(string data, bool makePredicatesSafe = false)
+ public char PartsSeparator { get; }
+ public char SubpartsSeparator { get; }
+ public char NegationIndicator { get; }
+ public char MessageIndicator { get; }
+
+ public OptionValueRestriction Parse(string data, bool makePredicatesSafe = false)
{
ExtendedArgumentException.ThrowIfNullOrWhiteSpace(data, nameof(data));
@@ -69,7 +83,7 @@ public static OptionValueRestriction Parse(string data, bool makePredicate
return new OptionValueRestriction(isValueAllowed, valueNotSatisfyRestrictionMessage);
}
- public static OptionValueRestriction> ParseForList(string data, bool makePredicatesSafe = false)
+ public OptionValueRestriction> ParseForList(string data, bool makePredicatesSafe = false)
{
ExtendedArgumentException.ThrowIfNullOrWhiteSpace(data, nameof(data));
@@ -97,6 +111,12 @@ private static Predicate MakeSafePredicate(Predicate predicate)
};
}
+ private static Predicate NegatePredicate(Predicate predicate)
+ {
+ ExtendedArgumentNullException.ThrowIfNull(predicate, nameof(predicate));
+ return t => !predicate(t);
+ }
+
private static Predicate CombinePredicates(List> predicates, List connections)
{
ExtendedArgumentNullException.ThrowIfNull(predicates, nameof(predicates));
@@ -145,40 +165,6 @@ private static bool TryParseLogicalOperator(string data, out LogicalOperator log
return result is not null;
}
- private static Predicate ParsePredicate(string[] data)
- {
- ExtendedArgumentNullException.ThrowIfNull(data, nameof(data));
-
- if (data.Length == 0)
- {
- throw new ArgumentException(
- $"Recieved data has incorrect format. Expected: {ExpectedFormat}",
- nameof(data));
- }
-
- string name = data[0];
- string[] parameters = [.. data.Skip(1)];
-
- return name.ToUpper(CultureInfo.CurrentCulture) switch
- {
- "EQUAL" or "==" or "=" => ParseEqualPredicate(parameters),
- "NOTEQUAL" or "!=" or "<>" => ParseNotEqualPredicate(parameters),
- "LESS" or "<" => ParseLessPredicate(parameters),
- "LESSOREQUAL" or "<=" => ParseLessOrEqualPredicate(parameters),
- "GREATER" or ">" => ParseGreaterPredicate(parameters),
- "GREATEROREQUAL" or ">=" => ParseGreaterOrEqualPredicate(parameters),
- "INRANGE" or "MINMAX" => ParseInRangePredicate(parameters),
- "ONEOF" or "INLIST" => ParseOneOfPredicate(parameters),
- "MATCH" or "REGEX" => ParseMatchPredicate(parameters),
- "DIRECTORYEXISTS" or "DIRECTORY" => ParseDirectoryExistsPredicate(parameters),
- "FILEEXISTS" or "FILE" => ParseFileExistsPredicate(parameters),
- "MAXFILESIZE" or "MAXSIZE" => ParseMaxFileSizePredicate(parameters),
- "EXTENSION" or "EXT" => ParseFileExtensionPredicate(parameters),
-
- _ => throw new ArgumentOutOfRangeException(nameof(data), "Unknown predicate name")
- };
- }
-
private static Predicate ParseComparePredicate(
string[] parameters,
Func compareFunc)
@@ -245,15 +231,46 @@ private static Predicate ParseOneOfPredicate(string[] parameters)
return value => parameters.Contains(value?.ToString() ?? string.Empty);
}
- private static Predicate ParseMatchPredicate(string[] parameters)
+ private static Predicate ParseDefaultPredicate(string[] parameters)
{
ExtendedArgumentNullException.ThrowIfNull(parameters, nameof(parameters));
- DefaultExceptions.ThrowIfEqual(parameters.Length, 0, nameof(parameters.Length));
+ DefaultExceptions.ThrowIfNotEqual(parameters.Length, 0, nameof(parameters.Length));
- string pattern = string.Join($"{SubpartsSeparator}", parameters);
+ return value => EqualityComparer.Default.Equals(value, default!);
+ }
- var regex = new Regex(pattern);
- return value => value is not null && regex.IsMatch(value.ToString() ?? string.Empty);
+ private static Predicate ParseNullPredicate(string[] parameters)
+ {
+ ExtendedArgumentNullException.ThrowIfNull(parameters, nameof(parameters));
+ DefaultExceptions.ThrowIfNotEqual(parameters.Length, 0, nameof(parameters.Length));
+
+ return value => value is null;
+ }
+
+ private static Predicate ParseNullOrEmptyPredicate(string[] parameters)
+ {
+ ExtendedArgumentNullException.ThrowIfNull(parameters, nameof(parameters));
+ DefaultExceptions.ThrowIfNotEqual(parameters.Length, 0, nameof(parameters.Length));
+
+ return value => value is null ||
+ (value is string text && string.IsNullOrEmpty(text));
+ }
+
+ private static Predicate ParseNullOrWhiteSpacePredicate(string[] parameters)
+ {
+ ExtendedArgumentNullException.ThrowIfNull(parameters, nameof(parameters));
+ DefaultExceptions.ThrowIfNotEqual(parameters.Length, 0, nameof(parameters.Length));
+
+ return value => value is null ||
+ (value is string text && string.IsNullOrWhiteSpace(text));
+ }
+
+ private static Predicate ParseEmptyPredicate(string[] parameters)
+ {
+ ExtendedArgumentNullException.ThrowIfNull(parameters, nameof(parameters));
+ DefaultExceptions.ThrowIfNotEqual(parameters.Length, 0, nameof(parameters.Length));
+
+ return value => value is string text && text.Length == 0;
}
private static Predicate ParseDirectoryExistsPredicate(string[] parameters)
@@ -333,4 +350,77 @@ private static Predicate ParseFileExtensionPredicate(string[] parameters)
}
};
}
+
+ private static Predicate ParseFilePredicate(string[] parameters)
+ {
+ ExtendedArgumentNullException.ThrowIfNull(parameters, nameof(parameters));
+
+ if (parameters.Length == 0)
+ return ParseFileExistsPredicate(parameters);
+
+ Predicate fileExistsPredicate = ParseFileExistsPredicate([]);
+ Predicate fileExtensionPredicate = ParseFileExtensionPredicate(parameters);
+
+ List> predicates = [fileExistsPredicate, fileExtensionPredicate];
+ List connections = [LogicalOperator.And];
+
+ return CombinePredicates(predicates, connections);
+ }
+
+ private Predicate ParseMatchPredicate(string[] parameters)
+ {
+ ExtendedArgumentNullException.ThrowIfNull(parameters, nameof(parameters));
+ DefaultExceptions.ThrowIfEqual(parameters.Length, 0, nameof(parameters.Length));
+
+ string pattern = string.Join($"{SubpartsSeparator}", parameters);
+
+ var regex = new Regex(pattern);
+ return value => value is not null && regex.IsMatch(value.ToString() ?? string.Empty);
+ }
+
+ private Predicate ParsePredicate(string[] data)
+ {
+ ExtendedArgumentNullException.ThrowIfNull(data, nameof(data));
+
+ if (data.Length == 0)
+ {
+ throw new ArgumentException(
+ $"Recieved data has incorrect format. Expected: {ExpectedFormat}",
+ nameof(data));
+ }
+
+ bool shouldNegatePredicate = data.Length > 0 && data[0].StartsWith(NegationIndicator);
+
+ string name = shouldNegatePredicate ? data[0].Substring(1) : data[0];
+ string[] parameters = [.. data.Skip(1)];
+
+ Predicate predicate = name.ToUpper(CultureInfo.CurrentCulture) switch
+ {
+ "EQUAL" or "==" or "=" => ParseEqualPredicate(parameters),
+ "NOTEQUAL" or "!=" or "<>" => ParseNotEqualPredicate(parameters),
+ "LESS" or "<" => ParseLessPredicate(parameters),
+ "LESSOREQUAL" or "MAX" or "<=" => ParseLessOrEqualPredicate(parameters),
+ "GREATER" or ">" => ParseGreaterPredicate(parameters),
+ "GREATEROREQUAL" or "MIN" or ">=" => ParseGreaterOrEqualPredicate(parameters),
+ "INRANGE" or "MINMAX" => ParseInRangePredicate(parameters),
+ "ONEOF" or "INLIST" => ParseOneOfPredicate(parameters),
+ "MATCH" or "REGEX" => ParseMatchPredicate(parameters),
+ "DEFAULT" => ParseDefaultPredicate(parameters),
+ "NULL" => ParseNullPredicate(parameters),
+ "NULLOREMPTY" => ParseNullOrEmptyPredicate(parameters),
+ "NULLORWHITESPACE" => ParseNullOrWhiteSpacePredicate(parameters),
+ "EMPTY" => ParseEmptyPredicate(parameters),
+ "DIRECTORYEXISTS" or "DIRECTORY" => ParseDirectoryExistsPredicate(parameters),
+ "FILEEXISTS" => ParseFileExistsPredicate(parameters),
+ "MAXFILESIZE" or "MAXSIZE" => ParseMaxFileSizePredicate(parameters),
+ "EXTENSION" or "EXT" => ParseFileExtensionPredicate(parameters),
+ "FILE" => ParseFilePredicate(parameters),
+
+ _ => throw new ArgumentOutOfRangeException(nameof(data), "Unknown predicate name")
+ };
+
+ return shouldNegatePredicate
+ ? NegatePredicate(predicate)
+ : predicate;
+ }
}
diff --git a/Core/NetArgumentParser/Options/IValueOption.cs b/Core/NetArgumentParser/Options/IValueOption.cs
index 1147bdd..566f537 100644
--- a/Core/NetArgumentParser/Options/IValueOption.cs
+++ b/Core/NetArgumentParser/Options/IValueOption.cs
@@ -13,8 +13,8 @@ public interface IValueOption : ICommonOption, IValueOptionDescriptionDesigne
string MetaVariable { get; }
IReadOnlyCollection Choices { get; }
- DefaultOptionValue? DefaultValue { get; }
- OptionValueRestriction? ValueRestriction { get; }
+ DefaultOptionValue? DefaultValue { get; set; }
+ OptionValueRestriction? ValueRestriction { get; set; }
IValueConverter? Converter { get; set; }
void HandleDefaultValue();
diff --git a/Documentation/ParserGenerationUsingAttributes.md b/Documentation/ParserGenerationUsingAttributes.md
index 3605e0d..58e5656 100644
--- a/Documentation/ParserGenerationUsingAttributes.md
+++ b/Documentation/ParserGenerationUsingAttributes.md
@@ -179,22 +179,30 @@ logical_operator predicateK_name parameter1 ... parameterM
In other words, a string consists of one or more predicates, each separated by a newline character `\n`. Following the predicate name, its parameters are listed. A logical connective (`AND` or `OR`) may precede the predicate name; if omitted, it defaults to `AND`. A line that begins with the `?` character specifies a message to be displayed if the option value doesn't satisfy the restriction. However, this message can be omitted.
+In addition, you can negate predicate. To do this, place `!` before the predicate name.
+
It is also important to note that parentheses are not supported, and logical connectives will be evaluated in the order in which they are specified. Thus, `x OR y AND z` will actually be interpreted as `(x OR y) AND z`. Additionally, logical connectives have aliases: for `OR`, they are `||` and `|`, while for `AND`, they are `&&` and `&`.
The following predicates are available:
1. `equal` (`==`, `=`): takes a single parameter. The option value must equal this parameter. The parameter type must be double, and the option value type must have the overloaded `==` operator.
2. `notequal` (`!=`, `<>`): takes a single parameter. The option value must differ from this parameter. The parameter type must be double, and the option value type must have the overloaded `!=` operator.
3. `less` (`<`): takes a single parameter. The option value must be less than this parameter. The parameter type must be double, and the option value type must have the overloaded `<` operator.
-4. `lessorequal` (`<=`): takes a single parameter. The option value must be less than or equal to this parameter. The parameter type must be double, and the option value type must have the overloaded `<=` operator.
+4. `lessorequal` (`max`, `<=`): takes a single parameter. The option value must be less than or equal to this parameter. The parameter type must be double, and the option value type must have the overloaded `<=` operator.
5. `greater` (`>`): takes a single parameter. The option value must be greater than this parameter. The parameter type must be double, and the option value type must have the overloaded `>` operator.
-6. `greaterorequal` (`>=`): takes a single parameter. The option value must be greater than or equal to this parameter. The parameter type must be double, and the option value type must have the overloaded `>=` operator.
+6. `greaterorequal` (`min`, `>=`): takes a single parameter. The option value must be greater than or equal to this parameter. The parameter type must be double, and the option value type must have the overloaded `>=` operator.
7. `inrange` (`minmax`): takes two parameters. The option value must be greater than or equal to the first parameter and less than or equal to the second parameter. The parameter types must be double, and the option value type must have the overloaded `>=` and `<=` operators.
8. `oneof` (`inlist`): takes one or more parameters. The option value, converted to a string, must equal one of the specified parameters.
9. `match` (`regex`): takes a single parameter. The option value, converted to a string, must match the specified parameter representing a regular expression. Anything written after the first space will be avaluated as a regular expression, so it can contain spaces.
-10. `directoryexists` (`directory`): takes no parameters. The option value must be a string representing the path to an existing directory.
-11. `fileexists` (`file`): takes no parameters. The option value must be a string representing the path to an existing file.
-12. `maxfilesize` (`maxsize`): takes a single parameter. The option value must be a string representing the path to a file whose size is less than or equal to this parameter (in bytes).
-13. `extension` (`ext`): takes one or more parameters. The option value must be a string representing the path to a file whose extension matches one of the specified parameters. The dot in the file extension is optional, and its case (uppercase or lowercase) doesn't matter.
+10. `default`: takes no parameters. The option value must be the default for corresponding type.
+11. `null`: takes no parameters. The option value must be null.
+12. `nullorempty`: takes no parameters. The option value must be null or empty string.
+13. `nullorwhitespace`: takes no parameters. The option value must be null, an empty string, or a string consisting only of whitespace characters.
+14. `empty`: takes no parameters. The option value must be an empty string.
+15. `directoryexists` (`directory`): takes no parameters. The option value must be a string representing the path to an existing directory.
+16. `fileexists`: takes no parameters. The option value must be a string representing the path to an existing file.
+17. `maxfilesize` (`maxsize`): takes a single parameter. The option value must be a string representing the path to a file whose size is less than or equal to this parameter (in bytes).
+18. `extension` (`ext`): takes one or more parameters. The option value must be a string representing the path to a file whose extension matches one of the specified parameters. The dot in the file extension is optional, and its case (uppercase or lowercase) doesn't matter.
+19. `file`: takes zero or more parameters. The option value must be a string representing the path to an existing file whose extension matches one of the specified parameters. If no parameters are provided, only the file's existence is checked. The dot in the file extension is optional, and its case (uppercase or lowercase) doesn't matter.
Examples of simple restrictions are provided below:
1. `== 5`: the option value must be equal to 5.
@@ -204,15 +212,25 @@ Examples of simple restrictions are provided below:
5. `> 5`: the option value must be greater than 5.
6. `>= 5`: the option value must be greater than or equal to 5.
7. `inrange 0 5`: the option value must be within the range from 0 to 5.
-8. `oneof 1 3 6`: the option value must be one of the values: 1, 3 or 6.
+8. `oneof 1 3 6`: the option value must be one of: 1, 3 or 6.
9. `match ^[A-Z][a-z]*$`: the option value must match the regular expression `^[A-Z][a-z]*$`.
-10. `directoryexists`: the option value must be a string representing the path to an existing directory.
-11. `fileexists`: the option value must be a string representing the path to an existing file.
-12. `maxfilesize 10240`: the option value must be a string representing the path to a file whose size is less than or equal to 10240 bytes.
-13. `extension jpg png`: the option value must be a string representing the path to a file whose extension matches either `jpg` or `png`.
+10. `default`: the option value must be the default for corresponding type.
+11. `null`: the option value must be null.
+12. `nullorempty`: the option value must be null or an empty string.
+13. `nullorwhitespace`: the option value must be null, an empty string, or a string consisting only of whitespace characters.
+14. `empty`: the option value must be an empty string.
+15. `directoryexists`: the option value must be a string representing the path to an existing directory.
+16. `fileexists`: the option value must be a string representing the path to an existing file.
+17. `maxfilesize 10240`: the option value must be a string representing the path to a file whose size is less than or equal to 10240 bytes.
+18. `extension jpg png`: the option value must be a string representing the path to a file whose extension matches either `jpg` or `png`.
+19. `file mp4 mkv`: the option value must be a string representing the path to an existing file whose extension matches either `mp4` or `mkv`.
+
+Examples of restrictions with negation are provided below:
+1. `!oneof 1 3 6`: the option value mustn't be one of: 1, 3 or 6.
+2. `!nullorempty`: the option value mustn't be null or an empty string.
Examples of complex restrictions are provided below:
-1. `< -100\nOR > 100\nOR oneof 1 5 7 10\nAND inrange -200 200`.
+1. `< -100\nOR > 100\nOR oneof 1 5 7 10\nAND inrange -200 200\nAND !oneof 77 -77 88`.
2. `fileexists\n&& extension jpg png\n?file must exists and be an image`.
Finally, here is an example of creating an option with a restriction using an attribute:
diff --git a/README.md b/README.md
index ed97201..809fe7e 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@
-
+
diff --git a/TODO.md b/TODO.md
index 81ee97e..ae16278 100644
--- a/TODO.md
+++ b/TODO.md
@@ -3,6 +3,7 @@ This is the TODO file where you can find some features and content that need to
## Features
- [ ] Come up with new features.
+- [ ] Add support of `--`.
- [ ] Add mutually inclusive groups.
- [ ] Add the ability to sort options (by name, by weight, etc) in application description generator.
- [ ] Add XML documentation.
diff --git a/Tests/NetArgumentParser.Tests/Models/Configurations/OptionValueRestrictionParserGeneratorConfig.cs b/Tests/NetArgumentParser.Tests/Models/Configurations/OptionValueRestrictionParserGeneratorConfig.cs
index 6bd2bd5..c6b1c66 100644
--- a/Tests/NetArgumentParser.Tests/Models/Configurations/OptionValueRestrictionParserGeneratorConfig.cs
+++ b/Tests/NetArgumentParser.Tests/Models/Configurations/OptionValueRestrictionParserGeneratorConfig.cs
@@ -41,6 +41,27 @@ internal partial class OptionValueRestrictionParserGeneratorConfig
public const string PhoneLongName = "Phone";
public const string? PhoneValueRestriction = $"match {PhonePattern}";
+ public const string DefaultValueTypeLongName = "DefaultValueType";
+ public const string? DefaultValueTypeValueRestriction = "default";
+
+ public const string DefaultReferenceTypeLongName = "DefaultReferenceType";
+ public const string? DefaultReferenceTypeValueRestriction = "default";
+
+ public const string NullStringLongName = "NullString";
+ public const string? NullStringValueRestriction = "null";
+
+ public const string NullOrEmptyStringLongName = "NullOrEmptyString";
+ public const string? NullOrEmptyStringValueRestriction = "nullorempty";
+
+ public const string NullOrWhiteSpaceStringLongName = "NullOrWhiteSpaceString";
+ public const string? NullOrWhiteSpaceStringValueRestriction = "nullorwhitespace";
+
+ public const string NotNullOrWhiteSpaceStringLongName = "NotNullOrWhiteSpaceString";
+ public const string? NotNullOrWhiteSpaceStringValueRestriction = "!nullorwhitespace";
+
+ public const string EmptyStringLongName = "EmptyString";
+ public const string? EmptyStringValueRestriction = "empty";
+
public const string OutputFilePathLongName = "OutputFilePath";
public const string? OutputFilePathValueRestriction = "extension jpg .png GiF";
@@ -48,9 +69,8 @@ internal partial class OptionValueRestrictionParserGeneratorConfig
public const string? ModeValueRestriction = $"oneof {nameof(FileMode.Append)} {nameof(FileMode.Open)}";
public const string NumbersLongName = "Numbers";
- public const string? NumbersValueRestriction = "< -100\nOR > 100\nOR oneof 1 5 7 10\nAND inrange -200 200";
+ public const string? NumbersValueRestriction = "< -100\nOR > 100\nOR oneof 1 5 7 10\nAND inrange -200 200\nAND !oneof 77 -77 88";
-#pragma warning disable SYSLIB1045 // Use GeneratedRegexAttribute to generate the regular expression implementation at compile time
public static Predicate AngleValueRestrictionPredicate { get; } = t => t == 45;
public static Predicate WeightValueRestrictionPredicate { get; } = t => t != 100;
public static Predicate AgeValueRestrictionPredicate { get; } = t => t < 20;
@@ -58,10 +78,28 @@ internal partial class OptionValueRestrictionParserGeneratorConfig
public static Predicate HeightValueRestrictionPredicate { get; } = t => t > 22;
public static Predicate LengthValueRestrictionPredicate { get; } = t => t >= 23;
public static Predicate VerbosityValueRestrictionPredicate { get; } = t => t >= 0 && t <= 4;
+
+#pragma warning disable SYSLIB1045 // Use GeneratedRegexAttribute to generate the regular expression implementation at compile time
public static Predicate NameValueRestrictionPredicate { get; } = t => new Regex(NamePattern).IsMatch(t);
public static Predicate PhoneValueRestrictionPredicate { get; } = t => new Regex(PhonePattern).IsMatch(t);
#pragma warning restore SYSLIB1045 // Use GeneratedRegexAttribute to generate the regular expression implementation at compile time
+ public static Predicate DefaultValueTypeValueRestrictionPredicate { get; } = t =>
+ {
+ return EqualityComparer.Default.Equals(t, default);
+ };
+
+ public static Predicate DefaultReferenceTypeValueRestrictionPredicate { get; } = t =>
+ {
+ return EqualityComparer.Default.Equals(t, default);
+ };
+
+ public static Predicate NullStringValueRestrictionPredicate { get; } = t => t is null;
+ public static Predicate NullOrEmptyStringValueRestrictionPredicate { get; } = string.IsNullOrEmpty;
+ public static Predicate NullOrWhiteSpaceStringValueRestrictionPredicate { get; } = string.IsNullOrWhiteSpace;
+ public static Predicate NotNullOrWhiteSpaceStringValueRestrictionPredicate { get; } = t => !string.IsNullOrWhiteSpace(t);
+ public static Predicate EmptyStringValueRestrictionPredicate { get; } = t => t is not null && t.Length == 0;
+
public static Predicate OutputFilePathValueRestrictionPredicate { get; } = t =>
{
var allowedExtensions = new List { ".JPG", ".PNG", ".GIF" };
@@ -89,7 +127,8 @@ internal partial class OptionValueRestrictionParserGeneratorConfig
static bool Greater(Number t) => t > 100;
static bool OneOf(Number t) => new List() { 1, 5, 7, 10 }.Any(x => t == x);
static bool InRange(Number t) => t >= -200 && t <= 200;
- static bool CombinedPredicate(Number t) => (Less(t) || Greater(t) || OneOf(t)) && InRange(t);
+ static bool NotOneOf(Number t) => !new List() { 77, -77, 88 }.Any(x => t == x);
+ static bool CombinedPredicate(Number t) => (Less(t) || Greater(t) || OneOf(t)) && InRange(t) && NotOneOf(t);
return t.All(CombinedPredicate);
};
@@ -148,6 +187,48 @@ internal partial class OptionValueRestrictionParserGeneratorConfig
]
public string? Phone { get; set; }
+ [ValueOption(
+ DefaultValueTypeLongName,
+ valueRestriction: DefaultValueTypeValueRestriction)
+ ]
+ public Point DefaultValueType { get; set; }
+
+ [ValueOption(
+ DefaultReferenceTypeLongName,
+ valueRestriction: DefaultReferenceTypeValueRestriction)
+ ]
+ public string? DefaultReferenceType { get; set; }
+
+ [ValueOption(
+ NullStringLongName,
+ valueRestriction: NullStringValueRestriction)
+ ]
+ public string? NullString { get; set; }
+
+ [ValueOption(
+ NullOrEmptyStringLongName,
+ valueRestriction: NullOrEmptyStringValueRestriction)
+ ]
+ public string? NullOrEmptyString { get; set; }
+
+ [ValueOption(
+ NullOrWhiteSpaceStringLongName,
+ valueRestriction: NullOrWhiteSpaceStringValueRestriction)
+ ]
+ public string? NullOrWhiteSpaceString { get; set; }
+
+ [ValueOption(
+ NotNullOrWhiteSpaceStringLongName,
+ valueRestriction: NotNullOrWhiteSpaceStringValueRestriction)
+ ]
+ public string? NotNullOrWhiteSpaceString { get; set; }
+
+ [ValueOption(
+ EmptyStringLongName,
+ valueRestriction: EmptyStringValueRestriction)
+ ]
+ public string? EmptyString { get; set; }
+
[ValueOption(
OutputFilePathLongName,
valueRestriction: OutputFilePathValueRestriction)
diff --git a/Tests/NetArgumentParser.Tests/ParserGeneratorTests.cs b/Tests/NetArgumentParser.Tests/ParserGeneratorTests.cs
index c084b58..a61edee 100644
--- a/Tests/NetArgumentParser.Tests/ParserGeneratorTests.cs
+++ b/Tests/NetArgumentParser.Tests/ParserGeneratorTests.cs
@@ -492,6 +492,41 @@ public void ConfigureParser_AllValueOptionTypes_ValueRestrictionsConfiguredCorre
false,
out IValueOption? phoneOption);
+ bool defaultValueTypeOptionFound = argumentParser.FindFirstValueOptionByLongName(
+ OptionValueRestrictionParserGeneratorConfig.DefaultValueTypeLongName,
+ false,
+ out IValueOption? defaultValueTypeOption);
+
+ bool defaultReferenceTypeOptionFound = argumentParser.FindFirstValueOptionByLongName(
+ OptionValueRestrictionParserGeneratorConfig.DefaultReferenceTypeLongName,
+ false,
+ out IValueOption? defaultReferenceTypeOption);
+
+ bool nullStringOptionFound = argumentParser.FindFirstValueOptionByLongName(
+ OptionValueRestrictionParserGeneratorConfig.NullStringLongName,
+ false,
+ out IValueOption? nullStringOption);
+
+ bool nullOrEmptyStringOptionFound = argumentParser.FindFirstValueOptionByLongName(
+ OptionValueRestrictionParserGeneratorConfig.NullOrEmptyStringLongName,
+ false,
+ out IValueOption? nullOrEmptyStringOption);
+
+ bool nullOrWhiteSpaceStringOptionFound = argumentParser.FindFirstValueOptionByLongName(
+ OptionValueRestrictionParserGeneratorConfig.NullOrWhiteSpaceStringLongName,
+ false,
+ out IValueOption? nullOrWhiteSpaceStringOption);
+
+ bool notNullOrWhiteSpaceStringOptionFound = argumentParser.FindFirstValueOptionByLongName(
+ OptionValueRestrictionParserGeneratorConfig.NotNullOrWhiteSpaceStringLongName,
+ false,
+ out IValueOption? notNullOrWhiteSpaceStringOption);
+
+ bool emptyStringOptionFound = argumentParser.FindFirstValueOptionByLongName(
+ OptionValueRestrictionParserGeneratorConfig.EmptyStringLongName,
+ false,
+ out IValueOption? emptyStringOption);
+
bool outputFilePathOptionFound = argumentParser.FindFirstValueOptionByLongName(
OptionValueRestrictionParserGeneratorConfig.OutputFilePathLongName,
false,
@@ -516,6 +551,13 @@ public void ConfigureParser_AllValueOptionTypes_ValueRestrictionsConfiguredCorre
Assert.True(verbosityOptionFound);
Assert.True(nameOptionFound);
Assert.True(phoneOptionFound);
+ Assert.True(defaultValueTypeOptionFound);
+ Assert.True(defaultReferenceTypeOptionFound);
+ Assert.True(nullStringOptionFound);
+ Assert.True(nullOrEmptyStringOptionFound);
+ Assert.True(nullOrWhiteSpaceStringOptionFound);
+ Assert.True(notNullOrWhiteSpaceStringOptionFound);
+ Assert.True(emptyStringOptionFound);
Assert.True(outputFilePathOptionFound);
Assert.True(modeOptionFound);
Assert.True(numbersOptionFound);
@@ -529,6 +571,13 @@ public void ConfigureParser_AllValueOptionTypes_ValueRestrictionsConfiguredCorre
Assert.NotNull(verbosityOption);
Assert.NotNull(nameOption);
Assert.NotNull(phoneOption);
+ Assert.NotNull(defaultValueTypeOption);
+ Assert.NotNull(defaultReferenceTypeOption);
+ Assert.NotNull(nullStringOption);
+ Assert.NotNull(nullOrEmptyStringOption);
+ Assert.NotNull(nullOrWhiteSpaceStringOption);
+ Assert.NotNull(notNullOrWhiteSpaceStringOption);
+ Assert.NotNull(emptyStringOption);
Assert.NotNull(outputFilePathOption);
Assert.NotNull(modeOption);
Assert.NotNull(numbersOption);
@@ -542,6 +591,13 @@ public void ConfigureParser_AllValueOptionTypes_ValueRestrictionsConfiguredCorre
Assert.NotNull(verbosityOption.ValueRestriction);
Assert.NotNull(nameOption.ValueRestriction);
Assert.NotNull(phoneOption.ValueRestriction);
+ Assert.NotNull(defaultValueTypeOption.ValueRestriction);
+ Assert.NotNull(defaultReferenceTypeOption.ValueRestriction);
+ Assert.NotNull(nullStringOption.ValueRestriction);
+ Assert.NotNull(nullOrEmptyStringOption.ValueRestriction);
+ Assert.NotNull(nullOrWhiteSpaceStringOption.ValueRestriction);
+ Assert.NotNull(notNullOrWhiteSpaceStringOption.ValueRestriction);
+ Assert.NotNull(emptyStringOption.ValueRestriction);
Assert.NotNull(outputFilePathOption.ValueRestriction);
Assert.NotNull(modeOption.ValueRestriction);
Assert.NotNull(numbersOption.ValueRestriction);
@@ -644,6 +700,60 @@ public void ConfigureParser_AllValueOptionTypes_ValueRestrictionsConfiguredCorre
phoneOption.ValueRestriction.IsValueAllowed.Invoke(phone));
}
+ for (int x = -10; x <= 10; ++x)
+ {
+ for (int y = -10; y <= 10; ++y)
+ {
+ var point = new Point(x, y);
+
+ Assert.Equal(
+ OptionValueRestrictionParserGeneratorConfig
+ .DefaultValueTypeValueRestrictionPredicate.Invoke(point),
+ defaultValueTypeOption.ValueRestriction.IsValueAllowed.Invoke(point));
+ }
+ }
+
+ string?[] texts =
+ [
+ null,
+ default,
+ string.Empty,
+ "+1 123 345 67 89",
+ "31233456789",
+ "-a bbbb cDe45 67 8D",
+ ".",
+ " ",
+ " ",
+ " a "
+ ];
+
+ foreach (string? text in texts)
+ {
+ Assert.Equal(
+ OptionValueRestrictionParserGeneratorConfig.DefaultReferenceTypeValueRestrictionPredicate.Invoke(text),
+ defaultReferenceTypeOption.ValueRestriction.IsValueAllowed.Invoke(text!));
+
+ Assert.Equal(
+ OptionValueRestrictionParserGeneratorConfig.NullStringValueRestrictionPredicate.Invoke(text),
+ nullStringOption.ValueRestriction.IsValueAllowed.Invoke(text!));
+
+ Assert.Equal(
+ OptionValueRestrictionParserGeneratorConfig.NullOrEmptyStringValueRestrictionPredicate.Invoke(text),
+ nullOrEmptyStringOption.ValueRestriction.IsValueAllowed.Invoke(text!));
+
+ Assert.Equal(
+ OptionValueRestrictionParserGeneratorConfig.NullOrWhiteSpaceStringValueRestrictionPredicate.Invoke(text),
+ nullOrWhiteSpaceStringOption.ValueRestriction.IsValueAllowed.Invoke(text!));
+
+ Assert.Equal(
+ OptionValueRestrictionParserGeneratorConfig.NotNullOrWhiteSpaceStringValueRestrictionPredicate.Invoke(text),
+ notNullOrWhiteSpaceStringOption.ValueRestriction.IsValueAllowed.Invoke(text!));
+
+ Assert.Equal(
+ OptionValueRestrictionParserGeneratorConfig.EmptyStringValueRestrictionPredicate.Invoke(text),
+ emptyStringOption.ValueRestriction.IsValueAllowed.Invoke(text!));
+ }
+
string[] outputFilePaths =
[
"/home/user/text.txt",