Implement gradle spec reader/writer
This commit is contained in:
		| @@ -427,6 +427,9 @@ SET(MULTIMC_SOURCES | ||||
| 	# RW lock protected map | ||||
| 	logic/RWStorage.h | ||||
|  | ||||
| 	# A variable that has an implicit default value and keeps track of changes | ||||
| 	logic/DefaultVariable.h | ||||
|  | ||||
| 	# network stuffs | ||||
| 	logic/net/NetAction.h | ||||
| 	logic/net/MD5EtagDownload.h | ||||
| @@ -493,6 +496,7 @@ SET(MULTIMC_SOURCES | ||||
| 	logic/OneSixInstance_p.h | ||||
|  | ||||
| 	# OneSix version json infrastructure | ||||
| 	logic/minecraft/GradleSpecifier.h | ||||
| 	logic/minecraft/InstanceVersion.cpp | ||||
| 	logic/minecraft/InstanceVersion.h | ||||
| 	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; | ||||
| 					record.name = attrs.value("name").toString(); | ||||
| 					record.logo = attrs.value("logo").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(); | ||||
| 					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,6 +65,9 @@ void OneSixLibrary::finalize() | ||||
| 	m_decentname = parts[1]; | ||||
| 	m_decentversion = minVersion = parts[2]; | ||||
| 	m_storage_path = 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()) | ||||
|   | ||||
| @@ -13,20 +13,20 @@ RawLibraryPtr RawLibrary::fromJson(const QJsonObject &libObj, const QString &fil | ||||
| 	} | ||||
| 	out->m_name = libObj.value("name").toString(); | ||||
|  | ||||
| 	auto readString = [libObj, filename](const QString & key, QString & variable) | ||||
| 	{ | ||||
| 		if (libObj.contains(key)) | ||||
| 	auto readString = [libObj, filename](const QString & key, QString & variable) -> bool | ||||
| 	{ | ||||
| 		if (!libObj.contains(key)) | ||||
| 			return false; | ||||
| 		QJsonValue val = libObj.value(key); | ||||
|  | ||||
| 		if (!val.isString()) | ||||
| 		{ | ||||
| 			QLOG_WARN() << key << "is not a string in" << filename << "(skipping)"; | ||||
| 			return false; | ||||
| 		} | ||||
| 			else | ||||
| 			{ | ||||
|  | ||||
| 		variable = val.toString(); | ||||
| 			} | ||||
| 		} | ||||
| 		return true; | ||||
| 	}; | ||||
|  | ||||
| 	readString("url", out->m_base_url); | ||||
|   | ||||
| @@ -28,10 +28,11 @@ public: /* methods */ | ||||
| 	 | ||||
| public: /* data */ | ||||
| 	QString m_name; | ||||
| 	QString m_base_url = "https://" + URLConstants::LIBRARY_BASE; | ||||
| 	QString m_base_url; | ||||
|  | ||||
| 	/// type hint - modifies how the library is treated | ||||
| 	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; | ||||
|  | ||||
| 	bool applyExcludes = false; | ||||
|   | ||||
| @@ -22,6 +22,7 @@ endmacro() | ||||
| # Tests START # | ||||
|  | ||||
| add_unit_test(pathutils tst_pathutils.cpp) | ||||
| add_unit_test(gradlespecifier tst_gradlespecifier.cpp) | ||||
| add_unit_test(userutils tst_userutils.cpp) | ||||
| add_unit_test(inifile tst_inifile.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