in src/NuGet.Core/NuGet.Versioning/VersionRangeFactory.cs [99:295]
public static bool TryParse(string value, bool allowFloating, [NotNullWhen(true)] out VersionRange? versionRange)
{
versionRange = null;
if (value is null)
{
return false;
}
lock (ParsedVersionRangeMapping)
{
if (ParsedVersionRangeMapping.TryGetValue((value, allowFloating), out versionRange))
{
return true;
}
}
var trimmedValue = value.Trim();
if (string.IsNullOrEmpty(trimmedValue))
{
return false;
}
var charArray = trimmedValue.ToCharArray();
// * is the only 1 char range
if (allowFloating
&& charArray.Length == 1
&& charArray[0] == '*')
{
versionRange = new VersionRange(new NuGetVersion(0, 0, 0), true, null, true, FloatRange.Parse(trimmedValue), originalString: value);
UpdateCachedVersionRange(value, allowFloating, versionRange);
return true;
}
string? minVersionString = null;
string? maxVersionString = null;
var isMinInclusive = false;
var isMaxInclusive = false;
NuGetVersion? minVersion = null;
NuGetVersion? maxVersion = null;
FloatRange? floatRange = null;
if (charArray[0] == '('
|| charArray[0] == '[')
{
// The first character must be [ to (
switch (charArray[0])
{
case '[':
isMinInclusive = true;
break;
case '(':
isMinInclusive = false;
break;
default:
return false;
}
// The last character must be ] ot )
switch (charArray[charArray.Length - 1])
{
case ']':
isMaxInclusive = true;
break;
case ')':
isMaxInclusive = false;
break;
default:
return false;
}
// Get rid of the two brackets
trimmedValue = trimmedValue.Substring(1, trimmedValue.Length - 2);
// Split by comma, and make sure we don't get more than two pieces
var parts = trimmedValue.Split(',');
if (parts.Length > 2)
{
return false;
}
else
{
var allEmpty = true;
for (int i = 0; i < parts.Length; i++)
{
if (!string.IsNullOrEmpty(parts[i]))
{
allEmpty = false;
break;
}
}
// If all parts are empty, then neither of upper or lower bounds were specified. Version spec is of the format (,]
if (allEmpty)
{
return false;
}
}
// (1.0.0] and [1.0.0),(1.0.0) are invalid.
if (parts.Length == 1
&& !(isMinInclusive && isMaxInclusive))
{
return false;
}
// If there is only one piece, we use it for both min and max
minVersionString = parts[0];
maxVersionString = (parts.Length == 2) ? parts[1] : parts[0];
}
else
{
// default to min inclusive when there are no braces
isMinInclusive = true;
// use the entire value as the version
minVersionString = trimmedValue;
}
if (!string.IsNullOrWhiteSpace(minVersionString))
{
// parse the min version string
#if NETCOREAPP2_1_OR_GREATER
if (allowFloating && minVersionString.Contains('*', StringComparison.Ordinal))
#else
if (allowFloating && minVersionString.Contains("*"))
#endif
{
// single floating version
if (FloatRange.TryParse(minVersionString, out floatRange)
&& floatRange.HasMinVersion)
{
minVersion = floatRange.MinVersion;
}
else
{
// invalid float
return false;
}
}
else
{
// single non-floating version
if (!NuGetVersion.TryParse(minVersionString, out minVersion))
{
// invalid version
return false;
}
}
}
// parse the max version string, the max cannot float
if (!string.IsNullOrWhiteSpace(maxVersionString))
{
if (!NuGetVersion.TryParse(maxVersionString, out maxVersion))
{
// invalid version
return false;
}
}
if (minVersion != null && maxVersion != null)
{
int result = minVersion.CompareTo(maxVersion);
// minVersion > maxVersion
if (result > 0)
{
return false;
}
// minVersion is equal to maxVersion (1.0.0, 1.0.0], [1.0.0, 1.0.0)
if (result == 0
&& (isMinInclusive ^ isMaxInclusive))
{
return false;
}
}
// Successful parse!
versionRange = new VersionRange(
minVersion: minVersion,
includeMinVersion: isMinInclusive,
maxVersion: maxVersion,
includeMaxVersion: isMaxInclusive,
floatRange: floatRange,
originalString: value);
UpdateCachedVersionRange(value, allowFloating, versionRange);
return true;
}