File indexing completed on 2024-12-22 04:15:09
0001 // This file is part of PyKrita, Krita' Python scripting plugin. 0002 // 0003 // SPDX-FileCopyrightText: 2013 Alex Turbov <i.zaufi@gmail.com> 0004 // 0005 // SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only 0006 0007 #ifndef __VERSION_CHECKER_H__ 0008 # define __VERSION_CHECKER_H__ 0009 0010 #include <libs/global/kis_debug.h> 0011 #include "utilities.h" 0012 # include <QtCore/QString> 0013 # include <QtCore/QStringList> 0014 # include <QtCore/QtGlobal> 0015 0016 namespace PyKrita 0017 { 0018 0019 /** 0020 * \brief Class \c version 0021 */ 0022 class version 0023 { 0024 enum type { 0025 undefined = -1, 0026 zero = 0 0027 }; 0028 0029 public: 0030 /// Default constructor 0031 explicit version(const int _major = zero, const int _minor = zero, const int patch = zero) 0032 : m_major(_major) 0033 , m_minor(_minor) 0034 , m_patch(patch) { 0035 } 0036 0037 int _major() const { 0038 return m_major; 0039 } 0040 int _minor() const { 0041 return m_minor; 0042 } 0043 int patch() const { 0044 return m_patch; 0045 } 0046 0047 bool isValid() const { 0048 return _major() != undefined && _minor() != undefined && patch() != undefined; 0049 } 0050 0051 operator QString() const { 0052 return QString("%1.%2.%3").arg(_major()).arg(_minor()).arg(patch()); 0053 } 0054 0055 static version fromString(const QString& version_str) { 0056 int tmp[3] = {zero, zero, zero}; 0057 QStringList parts = version_str.split('.'); 0058 for ( 0059 unsigned long i = 0 0060 ; i < qMin(static_cast<unsigned long>(sizeof(tmp) / sizeof(int)), static_cast<unsigned long>(parts.size())) 0061 ; ++i 0062 ) { 0063 bool ok; 0064 const int num = parts[i].toInt(&ok); 0065 if (ok) 0066 tmp[i] = num; 0067 else { 0068 tmp[i] = undefined; 0069 break; 0070 } 0071 } 0072 return version(tmp[0], tmp[1], tmp[2]); 0073 }; 0074 0075 static version fromPythonObject(PyObject* version_obj) 0076 { 0077 version v = tryObtainVersionFromTuple(version_obj); 0078 if (!v.isValid()) { 0079 // PEP396 requires __version__ to be a tuple of integers, 0080 // but some modules use a string instead. 0081 v = tryObtainVersionFromString(version_obj); 0082 } 0083 return v; 0084 } 0085 0086 static version invalid() { 0087 static version s_bad(undefined, undefined, undefined); 0088 return s_bad; 0089 } 0090 0091 private: 0092 int m_major; 0093 int m_minor; 0094 int m_patch; 0095 0096 0097 static version tryObtainVersionFromTuple(PyObject* version_obj) 0098 { 0099 Q_ASSERT("Sanity check" && version_obj); 0100 0101 if (PyTuple_Check(version_obj) == 0) 0102 return version::invalid(); 0103 0104 int version_info[3] = {0, 0, 0}; 0105 for (unsigned i = 0; i < PyTuple_Size(version_obj); ++i) { 0106 PyObject* v = PyTuple_GetItem(version_obj, i); 0107 if (v && PyLong_Check(v)) 0108 version_info[i] = PyLong_AsLong(v); 0109 else 0110 version_info[i] = -1; 0111 } 0112 if (version_info[0] != -1 && version_info[1] != -1 && version_info[2] != -1) 0113 return ::PyKrita::version(version_info[0], version_info[1], version_info[2]); 0114 0115 return version::invalid(); 0116 } 0117 0118 /** 0119 * Try to parse version string as a simple triplet X.Y.Z. 0120 * 0121 * \todo Some modules has letters in a version string... 0122 * For example current \c pytz version is \e "2013d". 0123 */ 0124 static version tryObtainVersionFromString(PyObject* version_obj) 0125 { 0126 Q_ASSERT("Sanity check" && version_obj); 0127 0128 if (!Python::isUnicode(version_obj)) 0129 return version::invalid(); 0130 0131 QString version_str = Python::unicode(version_obj); 0132 if (version_str.isEmpty()) 0133 return version::invalid(); 0134 0135 return version::fromString(version_str); 0136 } 0137 0138 0139 }; 0140 0141 inline bool operator==(const version& left, const version& right) 0142 { 0143 return left._major() == right._major() 0144 && left._minor() == right._minor() 0145 && left.patch() == right.patch() 0146 ; 0147 } 0148 0149 inline bool operator!=(const version& left, const version& right) 0150 { 0151 return !(left == right); 0152 } 0153 0154 inline bool operator<(const version& left, const version& right) 0155 { 0156 return left._major() < right._major() 0157 || (left._major() == right._major() && left._minor() < right._minor()) 0158 || (left._major() == right._major() && left._minor() == right._minor() && left.patch() < right.patch()) 0159 ; 0160 } 0161 0162 inline bool operator>(const version& left, const version& right) 0163 { 0164 return left._major() > right._major() 0165 || (left._major() == right._major() && left._minor() > right._minor()) 0166 || (left._major() == right._major() && left._minor() == right._minor() && left.patch() > right.patch()) 0167 ; 0168 } 0169 0170 inline bool operator<=(const version& left, const version& right) 0171 { 0172 return left == right || left < right; 0173 } 0174 0175 inline bool operator>=(const version& left, const version& right) 0176 { 0177 return left == right || left > right; 0178 } 0179 0180 0181 /** 0182 * \brief Class \c version_checker 0183 */ 0184 class version_checker 0185 { 0186 public: 0187 enum operation { 0188 invalid 0189 , undefined 0190 , less 0191 , less_or_equal 0192 , greater 0193 , greater_or_equal 0194 , not_equal 0195 , equal 0196 , last__ 0197 }; 0198 0199 /// Default constructor 0200 explicit version_checker(const operation op = invalid) 0201 : m_op(op) { 0202 } 0203 0204 bool isValid() const { 0205 return m_op != invalid; 0206 } 0207 0208 bool isEmpty() const { 0209 return m_op == undefined; 0210 } 0211 0212 void bind_second(const version& rhs) { 0213 m_rhs = rhs; 0214 } 0215 0216 bool operator()(const version& left) { 0217 switch (m_op) { 0218 case less: 0219 return left < m_rhs; 0220 case greater: 0221 return left > m_rhs; 0222 case equal: 0223 return left == m_rhs; 0224 case not_equal: 0225 return left != m_rhs; 0226 case less_or_equal: 0227 return left <= m_rhs; 0228 case greater_or_equal: 0229 return left >= m_rhs; 0230 default: 0231 Q_ASSERT(!"Sanity check"); 0232 break; 0233 } 0234 return false; 0235 } 0236 0237 version required() const { 0238 return m_rhs; 0239 } 0240 0241 QString operationToString() const { 0242 QString result; 0243 switch (m_op) { 0244 case less: 0245 result = " < "; 0246 break; 0247 case greater: 0248 result = " > "; 0249 break; 0250 case equal: 0251 result = " = "; 0252 break; 0253 case not_equal: 0254 result = " != "; 0255 break; 0256 case less_or_equal: 0257 result = " <= "; 0258 break; 0259 case greater_or_equal: 0260 result = " >= "; 0261 break; 0262 default: 0263 Q_ASSERT(!"Sanity check"); 0264 break; 0265 } 0266 return result; 0267 } 0268 0269 static version_checker fromString(const QString& version_info) { 0270 version_checker checker(invalid); 0271 if (version_info.isEmpty()) 0272 return checker; 0273 0274 bool lookup_next_char = false; 0275 int strip_lead_pos = 0; 0276 switch (version_info.at(0).toLatin1()) { 0277 case '<': 0278 checker.m_op = less; 0279 lookup_next_char = true; 0280 break; 0281 case '>': 0282 checker.m_op = greater; 0283 lookup_next_char = true; 0284 break; 0285 case '=': 0286 strip_lead_pos = 1; 0287 checker.m_op = equal; 0288 break; 0289 default: 0290 strip_lead_pos = 0; 0291 checker.m_op = equal; 0292 break; 0293 } 0294 if (lookup_next_char) { 0295 if (version_info.at(1).toLatin1() == '=') { 0296 // NOTE Shift state 0297 checker.m_op = operation(int(checker.m_op) + 1); 0298 strip_lead_pos = 2; 0299 } else { 0300 strip_lead_pos = 1; 0301 } 0302 } 0303 // 0304 QString rhs_str = version_info.mid(strip_lead_pos).trimmed(); 0305 version rhs = version::fromString(rhs_str); 0306 if (rhs.isValid()) 0307 checker.bind_second(rhs); 0308 else 0309 checker.m_op = invalid; 0310 return checker; 0311 } 0312 0313 private: 0314 operation m_op; 0315 version m_rhs; 0316 }; 0317 0318 } // namespace PyKrita 0319 #endif // __VERSION_CHECKER_H__