class Version implements Comparable { final int major; final int minor; final int? build; final int? revision; Version( this.major, this.minor, [ this.build, this.revision, ]); Version incrementMajor() => Version(major + 1, 0); Version incrementMinor() => Version(major, minor + 1); Version incrementBuild() => Version(major, minor, (build ?? 0) + 1); Version incrementRevision() => Version(major, minor, build, (revision ?? 0) + 1); static Version parse(String input) { final str = input.trim(); if (str.isEmpty) { throw const FormatException("Cannot parse empty string into version."); } final segments = str.split('.'); if (segments.length < 2) { throw const FormatException( "Format error: major and minor are required."); } if (segments.any((e) => !_isIntger(e))) { throw const FormatException("Format error: segment can only be intger."); } final len = segments.length; int _major = int.parse(segments[0]); int _minor = int.parse(segments[1]); int? _build; int? _revison; if (len > 2) _build = int.parse(segments[2]); if (len > 3) _revison = int.parse(segments[3]); return Version(_major, _minor, _build, _revison); } @override int compareTo(Version? other) { if (other == null) { throw ArgumentError.notNull("other"); } return _compare(this, other); } @override String toString() { final _build = build ?? (revision == null ? null : 0); return [major, minor, _build, revision] .where((e) => e != null) .join('.'); } @override bool operator ==(Object other) => other is Version && _compare(this, other) == 0; bool operator <(dynamic o) => o is Version && _compare(this, o) < 0; bool operator <=(dynamic o) => o is Version && _compare(this, o) <= 0; bool operator >(dynamic o) => o is Version && _compare(this, o) > 0; bool operator >=(dynamic o) => o is Version && _compare(this, o) >= 0; @override int get hashCode => toString().hashCode; static int _compare(Version? a, Version? b) { if (a == null) { throw ArgumentError.notNull("a"); } if (b == null) { throw ArgumentError.notNull("b"); } if (a.major > b.major) return 1; if (a.major < b.major) return -1; if (a.minor > b.minor) return 1; if (a.minor < b.minor) return -1; if (b.build != null) { if (a.build != null) { if (a.build! > b.build!) return 1; if (a.build! < b.build!) return -1; } else { if (b.build! > 0 && b.revision != null && b.revision! > 0) { return -1; } } } if (b.revision != null) { if (a.revision != null) { if (a.revision! > b.revision!) return 1; if (a.revision! < b.revision!) return -1; } else { if (b.revision! > 0) { return -1; } } } return 0; } static bool _isIntger(String? s) { if (s == null) { return false; } return int.tryParse(s) != null; } }