Implement gradle spec reader/writer
This commit is contained in:
		| @@ -427,6 +427,9 @@ SET(MULTIMC_SOURCES | |||||||
| 	# RW lock protected map | 	# RW lock protected map | ||||||
| 	logic/RWStorage.h | 	logic/RWStorage.h | ||||||
|  |  | ||||||
|  | 	# A variable that has an implicit default value and keeps track of changes | ||||||
|  | 	logic/DefaultVariable.h | ||||||
|  |  | ||||||
| 	# network stuffs | 	# network stuffs | ||||||
| 	logic/net/NetAction.h | 	logic/net/NetAction.h | ||||||
| 	logic/net/MD5EtagDownload.h | 	logic/net/MD5EtagDownload.h | ||||||
| @@ -493,6 +496,7 @@ SET(MULTIMC_SOURCES | |||||||
| 	logic/OneSixInstance_p.h | 	logic/OneSixInstance_p.h | ||||||
|  |  | ||||||
| 	# OneSix version json infrastructure | 	# OneSix version json infrastructure | ||||||
|  | 	logic/minecraft/GradleSpecifier.h | ||||||
| 	logic/minecraft/InstanceVersion.cpp | 	logic/minecraft/InstanceVersion.cpp | ||||||
| 	logic/minecraft/InstanceVersion.h | 	logic/minecraft/InstanceVersion.h | ||||||
| 	logic/minecraft/JarMod.cpp | 	logic/minecraft/JarMod.cpp | ||||||
|   | |||||||
							
								
								
									
										35
									
								
								logic/DefaultVariable.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								logic/DefaultVariable.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | template <typename T> | ||||||
|  | class DefaultVariable | ||||||
|  | { | ||||||
|  | public: | ||||||
|  | 	DefaultVariable(const T & value) | ||||||
|  | 	{ | ||||||
|  | 		defaultValue = value; | ||||||
|  | 	} | ||||||
|  | 	DefaultVariable<T> & operator =(const T & value) | ||||||
|  | 	{ | ||||||
|  | 		currentValue = value; | ||||||
|  | 		is_default = currentValue == defaultValue; | ||||||
|  | 		is_explicit = true; | ||||||
|  | 		return *this; | ||||||
|  | 	} | ||||||
|  | 	operator const T &() const | ||||||
|  | 	{ | ||||||
|  | 		return is_default ? defaultValue : currentValue; | ||||||
|  | 	} | ||||||
|  | 	bool isDefault() const | ||||||
|  | 	{ | ||||||
|  | 		return is_default; | ||||||
|  | 	} | ||||||
|  | 	bool isExplicit() const | ||||||
|  | 	{ | ||||||
|  | 		return is_explicit; | ||||||
|  | 	} | ||||||
|  | private: | ||||||
|  | 	T currentValue; | ||||||
|  | 	T defaultValue; | ||||||
|  | 	bool is_default = true; | ||||||
|  | 	bool is_explicit = false; | ||||||
|  | }; | ||||||
| @@ -331,7 +331,29 @@ QSet<FTBRecord> InstanceList::discoverFTBInstances() | |||||||
| 						continue; | 						continue; | ||||||
| 					record.name = attrs.value("name").toString(); | 					record.name = attrs.value("name").toString(); | ||||||
| 					record.logo = attrs.value("logo").toString(); | 					record.logo = attrs.value("logo").toString(); | ||||||
| 					record.mcVersion = attrs.value("mcVersion").toString(); | 					auto customVersions = attrs.value("customMCVersions"); | ||||||
|  | 					if(!customVersions.isNull()) | ||||||
|  | 					{ | ||||||
|  | 						QMap<QString, QString> versionMatcher; | ||||||
|  | 						QString customVersionsStr = customVersions.toString(); | ||||||
|  | 						QStringList list = customVersionsStr.split(';'); | ||||||
|  | 						for(auto item: list) | ||||||
|  | 						{ | ||||||
|  | 							auto segment = item.split('^'); | ||||||
|  | 							if(segment.size() != 2) | ||||||
|  | 							{ | ||||||
|  | 								QLOG_ERROR() << "FTB: Segment of size < 2 in " << customVersionsStr; | ||||||
|  | 								continue; | ||||||
|  | 							} | ||||||
|  | 							versionMatcher[segment[0]] = segment[1]; | ||||||
|  | 						} | ||||||
|  | 						auto actualVersion = attrs.value("version").toString(); | ||||||
|  | 						record.mcVersion = versionMatcher[actualVersion]; | ||||||
|  | 					} | ||||||
|  | 					else | ||||||
|  | 					{ | ||||||
|  | 						record.mcVersion = attrs.value("mcVersion").toString(); | ||||||
|  | 					} | ||||||
| 					record.description = attrs.value("description").toString(); | 					record.description = attrs.value("description").toString(); | ||||||
| 					records.insert(record); | 					records.insert(record); | ||||||
| 				} | 				} | ||||||
|   | |||||||
							
								
								
									
										83
									
								
								logic/minecraft/GradleSpecifier.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								logic/minecraft/GradleSpecifier.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <QString> | ||||||
|  | #include <QStringList> | ||||||
|  | #include "logic/DefaultVariable.h" | ||||||
|  |  | ||||||
|  | struct GradleSpecifier | ||||||
|  | { | ||||||
|  | 	GradleSpecifier() | ||||||
|  | 	{ | ||||||
|  | 		m_valid = false; | ||||||
|  | 	} | ||||||
|  | 	GradleSpecifier(QString value) | ||||||
|  | 	{ | ||||||
|  | 		operator=(value); | ||||||
|  | 	} | ||||||
|  | 	GradleSpecifier & operator =(const QString & value) | ||||||
|  | 	{ | ||||||
|  | 		/* | ||||||
|  | 		org.gradle.test.classifiers : service : 1.0 : jdk15 @ jar | ||||||
|  | 		DEBUG   0 "org.gradle.test.classifiers:service:1.0:jdk15@jar"  | ||||||
|  | 		DEBUG   1 "org.gradle.test.classifiers"  | ||||||
|  | 		DEBUG   2 "service"  | ||||||
|  | 		DEBUG   3 "1.0"  | ||||||
|  | 		DEBUG   4 ":jdk15"  | ||||||
|  | 		DEBUG   5 "jdk15"  | ||||||
|  | 		DEBUG   6 "@jar"  | ||||||
|  | 		DEBUG   7 "jar" | ||||||
|  | 		*/ | ||||||
|  | 		QRegExp matcher("([^:@]+):([^:@]+):([^:@]+)" "(:([^:@]+))?" "(@([^:@]+))?"); | ||||||
|  | 		m_valid = matcher.exactMatch(value); | ||||||
|  | 		auto elements = matcher.capturedTexts(); | ||||||
|  | 		groupId = elements[1]; | ||||||
|  | 		artifactId = elements[2]; | ||||||
|  | 		version = elements[3]; | ||||||
|  | 		classifier = elements[5]; | ||||||
|  | 		if(!elements[7].isEmpty()) | ||||||
|  | 		{ | ||||||
|  | 			extension = elements[7]; | ||||||
|  | 		} | ||||||
|  | 		return *this; | ||||||
|  | 	} | ||||||
|  | 	operator QString() const | ||||||
|  | 	{ | ||||||
|  | 		if(!m_valid) | ||||||
|  | 			return "INVALID"; | ||||||
|  | 		QString retval = groupId + ":" + artifactId + ":" + version; | ||||||
|  | 		if(!classifier.isEmpty()) | ||||||
|  | 		{ | ||||||
|  | 			retval += ":" + classifier; | ||||||
|  | 		} | ||||||
|  | 		if(extension.isExplicit()) | ||||||
|  | 		{ | ||||||
|  | 			retval += "@" + extension; | ||||||
|  | 		} | ||||||
|  | 		return retval; | ||||||
|  | 	} | ||||||
|  | 	QString toPath() const | ||||||
|  | 	{ | ||||||
|  | 		if(!m_valid) | ||||||
|  | 			return "INVALID"; | ||||||
|  | 		QString path = groupId; | ||||||
|  | 		path.replace('.', '/'); | ||||||
|  | 		path += '/' + artifactId + '/' + version + '/' + artifactId + '-' + version; | ||||||
|  | 		if(!classifier.isEmpty()) | ||||||
|  | 		{ | ||||||
|  | 			path += "-" + classifier; | ||||||
|  | 		} | ||||||
|  | 		path += "." + extension; | ||||||
|  | 		return path; | ||||||
|  | 	} | ||||||
|  | 	bool valid() | ||||||
|  | 	{ | ||||||
|  | 		return m_valid; | ||||||
|  | 	} | ||||||
|  | private: | ||||||
|  | 	QString groupId; | ||||||
|  | 	QString artifactId; | ||||||
|  | 	QString version; | ||||||
|  | 	QString classifier; | ||||||
|  | 	DefaultVariable<QString> extension = DefaultVariable<QString>("jar"); | ||||||
|  | 	bool m_valid = false; | ||||||
|  | }; | ||||||
| @@ -65,7 +65,10 @@ void OneSixLibrary::finalize() | |||||||
| 	m_decentname = parts[1]; | 	m_decentname = parts[1]; | ||||||
| 	m_decentversion = minVersion = parts[2]; | 	m_decentversion = minVersion = parts[2]; | ||||||
| 	m_storage_path = relative; | 	m_storage_path = relative; | ||||||
| 	m_download_url = m_base_url + relative; | 	if(m_base_url.isEmpty()) | ||||||
|  | 		m_download_url = QString("https://" + URLConstants::LIBRARY_BASE) + relative; | ||||||
|  | 	else | ||||||
|  | 		m_download_url = m_base_url + relative; | ||||||
|  |  | ||||||
| 	if (m_rules.empty()) | 	if (m_rules.empty()) | ||||||
| 	{ | 	{ | ||||||
|   | |||||||
| @@ -13,20 +13,20 @@ RawLibraryPtr RawLibrary::fromJson(const QJsonObject &libObj, const QString &fil | |||||||
| 	} | 	} | ||||||
| 	out->m_name = libObj.value("name").toString(); | 	out->m_name = libObj.value("name").toString(); | ||||||
|  |  | ||||||
| 	auto readString = [libObj, filename](const QString & key, QString & variable) | 	auto readString = [libObj, filename](const QString & key, QString & variable) -> bool | ||||||
| 	{ | 	{ | ||||||
| 		if (libObj.contains(key)) | 		if (!libObj.contains(key)) | ||||||
|  | 			return false; | ||||||
|  | 		QJsonValue val = libObj.value(key); | ||||||
|  |  | ||||||
|  | 		if (!val.isString()) | ||||||
| 		{ | 		{ | ||||||
| 			QJsonValue val = libObj.value(key); | 			QLOG_WARN() << key << "is not a string in" << filename << "(skipping)"; | ||||||
| 			if (!val.isString()) | 			return false; | ||||||
| 			{ |  | ||||||
| 				QLOG_WARN() << key << "is not a string in" << filename << "(skipping)"; |  | ||||||
| 			} |  | ||||||
| 			else |  | ||||||
| 			{ |  | ||||||
| 				variable = val.toString(); |  | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		variable = val.toString(); | ||||||
|  | 		return true; | ||||||
| 	}; | 	}; | ||||||
|  |  | ||||||
| 	readString("url", out->m_base_url); | 	readString("url", out->m_base_url); | ||||||
|   | |||||||
| @@ -28,10 +28,11 @@ public: /* methods */ | |||||||
| 	 | 	 | ||||||
| public: /* data */ | public: /* data */ | ||||||
| 	QString m_name; | 	QString m_name; | ||||||
| 	QString m_base_url = "https://" + URLConstants::LIBRARY_BASE; | 	QString m_base_url; | ||||||
|  |  | ||||||
| 	/// type hint - modifies how the library is treated | 	/// type hint - modifies how the library is treated | ||||||
| 	QString m_hint; | 	QString m_hint; | ||||||
| 	/// absolute URL. takes precedence over m_download_path, if defined | 	/// DEPRECATED: absolute URL. takes precedence over m_download_path, if defined | ||||||
| 	QString m_absolute_url; | 	QString m_absolute_url; | ||||||
|  |  | ||||||
| 	bool applyExcludes = false; | 	bool applyExcludes = false; | ||||||
|   | |||||||
| @@ -22,6 +22,7 @@ endmacro() | |||||||
| # Tests START # | # Tests START # | ||||||
|  |  | ||||||
| add_unit_test(pathutils tst_pathutils.cpp) | add_unit_test(pathutils tst_pathutils.cpp) | ||||||
|  | add_unit_test(gradlespecifier tst_gradlespecifier.cpp) | ||||||
| add_unit_test(userutils tst_userutils.cpp) | add_unit_test(userutils tst_userutils.cpp) | ||||||
| add_unit_test(inifile tst_inifile.cpp) | add_unit_test(inifile tst_inifile.cpp) | ||||||
| add_unit_test(UpdateChecker tst_UpdateChecker.cpp) | add_unit_test(UpdateChecker tst_UpdateChecker.cpp) | ||||||
|   | |||||||
							
								
								
									
										77
									
								
								tests/tst_gradlespecifier.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								tests/tst_gradlespecifier.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,77 @@ | |||||||
|  | #include <QTest> | ||||||
|  | #include "TestUtil.h" | ||||||
|  |  | ||||||
|  | #include "logic/minecraft/GradleSpecifier.h" | ||||||
|  |  | ||||||
|  | class GradleSpecifierTest : public QObject | ||||||
|  | { | ||||||
|  | 	Q_OBJECT | ||||||
|  | private | ||||||
|  | slots: | ||||||
|  | 	void initTestCase() | ||||||
|  | 	{ | ||||||
|  |  | ||||||
|  | 	} | ||||||
|  | 	void cleanupTestCase() | ||||||
|  | 	{ | ||||||
|  |  | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	void test_Positive_data() | ||||||
|  | 	{ | ||||||
|  | 		QTest::addColumn<QString>("through"); | ||||||
|  |  | ||||||
|  | 		QTest::newRow("3 parter") << "org.gradle.test.classifiers:service:1.0"; | ||||||
|  | 		QTest::newRow("classifier") << "org.gradle.test.classifiers:service:1.0:jdk15"; | ||||||
|  | 		QTest::newRow("jarextension") << "org.gradle.test.classifiers:service:1.0@jar"; | ||||||
|  | 		QTest::newRow("jarboth") << "org.gradle.test.classifiers:service:1.0:jdk15@jar"; | ||||||
|  | 		QTest::newRow("packxz") << "org.gradle.test.classifiers:service:1.0:jdk15@jar.pack.xz"; | ||||||
|  | 	} | ||||||
|  | 	void test_Positive() | ||||||
|  | 	{ | ||||||
|  | 		QFETCH(QString, through); | ||||||
|  |  | ||||||
|  | 		QString converted = GradleSpecifier(through); | ||||||
|  | 		 | ||||||
|  | 		QCOMPARE(converted, through); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	void test_Path_data() | ||||||
|  | 	{ | ||||||
|  | 		QTest::addColumn<QString>("spec"); | ||||||
|  | 		QTest::addColumn<QString>("expected"); | ||||||
|  |  | ||||||
|  | 		QTest::newRow("3 parter") << "group.id:artifact:1.0" << "group/id/artifact/1.0/artifact-1.0.jar"; | ||||||
|  | 		QTest::newRow("doom") << "id.software:doom:1.666:demons@wad" << "id/software/doom/1.666/doom-1.666-demons.wad"; | ||||||
|  | 	} | ||||||
|  | 	void test_Path() | ||||||
|  | 	{ | ||||||
|  | 		QFETCH(QString, spec); | ||||||
|  | 		QFETCH(QString, expected); | ||||||
|  |  | ||||||
|  | 		QString converted = GradleSpecifier(spec).toPath(); | ||||||
|  | 		 | ||||||
|  | 		QCOMPARE(converted, expected); | ||||||
|  | 	} | ||||||
|  | 	void test_Negative_data() | ||||||
|  | 	{ | ||||||
|  | 		QTest::addColumn<QString>("input"); | ||||||
|  |  | ||||||
|  | 		QTest::newRow("too many :") << "org:gradle.test:class:::ifiers:service:1.0::"; | ||||||
|  | 		QTest::newRow("nonsense") << "I like turtles"; | ||||||
|  | 		QTest::newRow("empty string") << ""; | ||||||
|  | 		QTest::newRow("missing version") << "herp.derp:artifact"; | ||||||
|  | 	} | ||||||
|  | 	void test_Negative() | ||||||
|  | 	{ | ||||||
|  | 		QFETCH(QString, input); | ||||||
|  |  | ||||||
|  | 		GradleSpecifier spec(input); | ||||||
|  | 		QVERIFY(!spec.valid()); | ||||||
|  | 		QCOMPARE(spec.operator QString(), QString("INVALID")); | ||||||
|  | 	} | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | QTEST_GUILESS_MAIN_MULTIMC(GradleSpecifierTest) | ||||||
|  |  | ||||||
|  | #include "tst_gradlespecifier.moc" | ||||||
		Reference in New Issue
	
	Block a user