VersionConstraint.parse constructor

VersionConstraint.parse(
  1. String text
)

Parses a version constraint.

This string is one of:

  • "any". any version.
  • "^" followed by a version string. Versions compatible with (VersionConstraint.compatibleWith) the version.
  • a series of version parts. Each part can be one of:
    • A version string like 1.2.3. In other words, anything that can be parsed by Version.parse().
    • A comparison operator (<, >, <=, or >=) followed by a version string.

Whitespace is ignored.

Examples:

any
^0.7.2
^1.0.0-alpha
1.2.3-alpha
<=5.1.4
>2.0.4 <= 2.4.6

Implementation

factory VersionConstraint.parse(String text) {
  var originalText = text;

  void skipWhitespace() {
    text = text.trim();
  }

  skipWhitespace();

  // Handle the "any" constraint.
  if (text == 'any') return any;

  // Try to parse and consume a version number.
  Version? matchVersion() {
    var version = startVersion.firstMatch(text);
    if (version == null) return null;

    text = text.substring(version.end);
    return Version.parse(version[0]!);
  }

  // Try to parse and consume a comparison operator followed by a version.
  VersionRange? matchComparison() {
    var comparison = startComparison.firstMatch(text);
    if (comparison == null) return null;

    var op = comparison[0];
    text = text.substring(comparison.end);
    skipWhitespace();

    var version = matchVersion();
    if (version == null) {
      throw FormatException('Expected version number after "$op" in '
          '"$originalText", got "$text".');
    }

    switch (op) {
      case '<=':
        return VersionRange(max: version, includeMax: true);
      case '<':
        return VersionRange(max: version, alwaysIncludeMaxPreRelease: true);
      case '>=':
        return VersionRange(min: version, includeMin: true);
      case '>':
        return VersionRange(min: version);
    }
    throw UnsupportedError(op!);
  }

  // Try to parse the "^" operator followed by a version.
  VersionConstraint? matchCompatibleWith() {
    if (!text.startsWith(compatibleWithChar)) return null;

    text = text.substring(compatibleWithChar.length);
    skipWhitespace();

    var version = matchVersion();
    if (version == null) {
      throw FormatException('Expected version number after '
          '"$compatibleWithChar" in "$originalText", got "$text".');
    }

    if (text.isNotEmpty) {
      throw FormatException('Cannot include other constraints with '
          '"$compatibleWithChar" constraint in "$originalText".');
    }

    return VersionConstraint.compatibleWith(version);
  }

  var compatibleWith = matchCompatibleWith();
  if (compatibleWith != null) return compatibleWith;

  Version? min;
  var includeMin = false;
  Version? max;
  var includeMax = false;

  for (;;) {
    skipWhitespace();

    if (text.isEmpty) break;

    var newRange = matchVersion() ?? matchComparison();
    if (newRange == null) {
      throw FormatException('Could not parse version "$originalText". '
          'Unknown text at "$text".');
    }

    if (newRange.min != null) {
      if (min == null || newRange.min! > min) {
        min = newRange.min;
        includeMin = newRange.includeMin;
      } else if (newRange.min == min && !newRange.includeMin) {
        includeMin = false;
      }
    }

    if (newRange.max != null) {
      if (max == null || newRange.max! < max) {
        max = newRange.max;
        includeMax = newRange.includeMax;
      } else if (newRange.max == max && !newRange.includeMax) {
        includeMax = false;
      }
    }
  }

  if (min == null && max == null) {
    throw const FormatException('Cannot parse an empty string.');
  }

  if (min != null && max != null) {
    if (min > max) return VersionConstraint.empty;
    if (min == max) {
      if (includeMin && includeMax) return min;
      return VersionConstraint.empty;
    }
  }

  return VersionRange(
      min: min, includeMin: includeMin, max: max, includeMax: includeMax);
}