blob: 595aa6aed7b51aec7065837bf607e1bc3ffa7714 [file] [log] [blame]
# Copyright (C) 2020 Apple Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import re
from webkitcorepy.string_utils import unicode
class Version(object):
MATCHES_RE = re.compile(r'(?P<operator>==|!=|>|<|>=|<=)? *(?P<version>\d+(\.\d+)?(\.\d+)?(\.\*)?)')
@staticmethod
def from_string(string):
assert isinstance(string, str) or isinstance(string, unicode)
return Version.from_iterable(string.split('.'))
@staticmethod
def from_iterable(val):
result = Version()
for i in range(len(val)):
result[i] = int(val[i])
return result
@staticmethod
def from_name(name):
from webkitpy.common.version_name_map import VersionNameMap
return VersionNameMap.map().from_name(name)[1]
def __init__(self, major=0, minor=0, tiny=0, micro=0, nano=0):
self.major = int(major)
self.minor = int(minor)
self.tiny = int(tiny)
self.micro = int(micro)
self.nano = int(nano)
def __len__(self):
return 5
def __getitem__(self, key):
if isinstance(key, int):
if key == 0:
return self.major
elif key == 1:
return self.minor
elif key == 2:
return self.tiny
elif key == 3:
return self.micro
elif key == 4:
return self.nano
raise ValueError('Version key must be between 0 and 4')
elif isinstance(key, str):
if hasattr(self, key):
return getattr(self, key)
raise ValueError('Version key must be major, minor, tiny, micro or nano')
raise ValueError('Expected version key to be string or integer')
def __setitem__(self, key, value):
if isinstance(key, int):
if key == 0:
self.major = int(value)
return self.major
elif key == 1:
self.minor = int(value)
return self.minor
elif key == 2:
self.tiny = int(value)
return self.tiny
elif key == 3:
self.micro = int(value)
return self.micro
elif key == 4:
self.nano = int(value)
return self.nano
raise ValueError('Version key must be between 0 and 4')
elif isinstance(key, str):
if hasattr(self, key):
return setattr(self, key, value)
raise ValueError('Version key must be major, minor, tiny, micro or nano')
raise ValueError('Expected version key to be string or integer')
def matches(self, expressions):
does_match = None
for expression in expressions.split(','):
match = self.MATCHES_RE.search(expression)
if not match:
continue
operator = match.group('operator') or '=='
if operator in ['==', '!=']:
version_match = True
split = match.group('version').split('.')
for index in range(len(split)):
if '*' == split[index]:
continue
if str(self[index]) != split[index]:
version_match = False
break
if operator == '==':
does_match = (does_match or False) | version_match
if version_match and operator == '!=':
return False
continue
version = self.from_string(match.group('version').replace('*', '0'))
does_match = (does_match or False) | {
'>': lambda a, b: a > b,
'<': lambda a, b: a < b,
'>=': lambda a, b: a >= b,
'<=': lambda a, b: a <= b,
}[operator](self, version)
return True if does_match is None else does_match
# 11.2 is in 11, but 11 is not in 11.2
def __contains__(self, version):
assert isinstance(version, Version)
does_match = True
for i in range(len(version)):
if self[i] != version[i]:
does_match = False
if not does_match and self[i] != 0:
return False
return True
def __repr__(self):
len_to_print = 1
for i in range(len(self)):
if self[i]:
len_to_print = i + 1
result = str(self.major)
for i in range(len_to_print - 1):
result += '.{}'.format(self[i + 1])
return result
def __hash__(self):
result = 0
for index in range(len(self)):
result *= 1000
result += self[index]
return result
def __cmp__(self, other):
if other is None:
return 1
for i in range(len(self)):
diff = self[i] - other[i]
if diff:
return diff
return 0
def __eq__(self, other):
return self.__cmp__(other) == 0
def __ne__(self, other):
return self.__cmp__(other) != 0
def __lt__(self, other):
return self.__cmp__(other) < 0
def __le__(self, other):
return self.__cmp__(other) <= 0
def __gt__(self, other):
return self.__cmp__(other) > 0
def __ge__(self, other):
return self.__cmp__(other) >= 0