From 0858c9007961e5eccadc39d429ea8d7d93a7fd56 Mon Sep 17 00:00:00 2001 From: comp500 Date: Tue, 15 Dec 2020 17:28:23 +0000 Subject: [PATCH] Rework error handling to be more robust --- build.gradle.kts | 2 - .../infra/packwiz/installer/DownloadTask.kt | 7 +- .../link/infra/packwiz/installer/Main.kt | 45 +++---- .../infra/packwiz/installer/UpdateManager.kt | 114 ++++++++---------- .../installer/request/HandlerManager.kt | 3 + .../packwiz/installer/ui/IUserInterface.kt | 9 +- .../packwiz/installer/ui/cli/CLIHandler.kt | 16 ++- .../packwiz/installer/ui/gui/GUIHandler.kt | 35 +++--- .../link/infra/packwiz/installer/util/Exts.kt | 38 ++++++ .../link/infra/packwiz/installer/util/Log.kt | 16 +++ 10 files changed, 170 insertions(+), 115 deletions(-) create mode 100644 src/main/kotlin/link/infra/packwiz/installer/util/Exts.kt create mode 100644 src/main/kotlin/link/infra/packwiz/installer/util/Log.kt diff --git a/build.gradle.kts b/build.gradle.kts index ad4d916..9b671c5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -14,8 +14,6 @@ java { dependencies { implementation("commons-cli:commons-cli:1.4") implementation("com.moandjiezana.toml:toml4j:0.7.2") - // TODO: Implement tests - //testImplementation "junit:junit:4.12" implementation("com.google.code.gson:gson:2.8.1") implementation("com.squareup.okio:okio:2.2.2") implementation(kotlin("stdlib-jdk8")) diff --git a/src/main/kotlin/link/infra/packwiz/installer/DownloadTask.kt b/src/main/kotlin/link/infra/packwiz/installer/DownloadTask.kt index 23c2408..8e04245 100644 --- a/src/main/kotlin/link/infra/packwiz/installer/DownloadTask.kt +++ b/src/main/kotlin/link/infra/packwiz/installer/DownloadTask.kt @@ -8,6 +8,7 @@ import link.infra.packwiz.installer.metadata.hash.HashUtils.getHash import link.infra.packwiz.installer.metadata.hash.HashUtils.getHasher import link.infra.packwiz.installer.ui.data.ExceptionDetails import link.infra.packwiz.installer.ui.data.IOptionDetails +import link.infra.packwiz.installer.util.Log import okio.Buffer import okio.HashingSink import okio.buffer @@ -125,6 +126,7 @@ internal class DownloadTask private constructor(val metadata: IndexFile.File, de fun download(packFolder: String, indexUri: SpaceSafeURI) { if (err != null) return + // TODO: is this necessary if we overwrite? // Ensure it is removed cachedFile?.let { if (!it.optionValue || !correctSide()) { @@ -133,8 +135,7 @@ internal class DownloadTask private constructor(val metadata: IndexFile.File, de try { Files.deleteIfExists(Paths.get(packFolder, it.cachedLocation)) } catch (e: IOException) { - // TODO: how much of a problem is this? use log4j/other log library to show warning? - e.printStackTrace() + Log.warn("Failed to delete file before downloading", e) } it.cachedLocation = null } @@ -190,7 +191,7 @@ internal class DownloadTask private constructor(val metadata: IndexFile.File, de Files.copy(data.inputStream(), destPath, StandardCopyOption.REPLACE_EXISTING) data.clear() } else { - // TODO: no more PRINTLN!!!!!!!!! + // TODO: move println to something visible in the error window println("Invalid hash for " + metadata.destURI.toString()) println("Calculated: " + fileSource.hash) println("Expected: $hash") diff --git a/src/main/kotlin/link/infra/packwiz/installer/Main.kt b/src/main/kotlin/link/infra/packwiz/installer/Main.kt index 72a4c10..1b7b91e 100644 --- a/src/main/kotlin/link/infra/packwiz/installer/Main.kt +++ b/src/main/kotlin/link/infra/packwiz/installer/Main.kt @@ -5,6 +5,7 @@ package link.infra.packwiz.installer import link.infra.packwiz.installer.metadata.SpaceSafeURI import link.infra.packwiz.installer.ui.cli.CLIHandler import link.infra.packwiz.installer.ui.gui.GUIHandler +import link.infra.packwiz.installer.util.Log import org.apache.commons.cli.DefaultParser import org.apache.commons.cli.Options import org.apache.commons.cli.ParseException @@ -29,7 +30,7 @@ class Main(args: Array) { val cmd = try { parser.parse(options, args) } catch (e: ParseException) { - e.printStackTrace() + Log.fatal("Failed to parse command line arguments", e) if (guiEnabled) { EventQueue.invokeAndWait { try { @@ -37,7 +38,8 @@ class Main(args: Array) { } catch (ignored: Exception) { // Ignore the exceptions, just continue using the ugly L&F } - JOptionPane.showMessageDialog(null, e.message, "packwiz-installer", JOptionPane.ERROR_MESSAGE) + JOptionPane.showMessageDialog(null, "Failed to parse command line arguments: $e", + "packwiz-installer", JOptionPane.ERROR_MESSAGE) } } exitProcess(1) @@ -51,33 +53,34 @@ class Main(args: Array) { val unparsedArgs = cmd.args if (unparsedArgs.size > 1) { - ui.handleExceptionAndExit(RuntimeException("Too many arguments specified!")) + ui.showErrorAndExit("Too many arguments specified!") } else if (unparsedArgs.isEmpty()) { - ui.handleExceptionAndExit(RuntimeException("URI to install from must be specified!")) + ui.showErrorAndExit("pack.toml URI to install from must be specified!") } - cmd.getOptionValue("title")?.also(ui::setTitle) + val title = cmd.getOptionValue("title") + if (title != null) { + ui.setTitle(title) + } ui.show() - val uOptions = UpdateManager.Options().apply { - side = cmd.getOptionValue("side")?.let((UpdateManager.Options.Side)::from) ?: side - packFolder = cmd.getOptionValue("pack-folder") ?: packFolder - manifestFile = cmd.getOptionValue("meta-file") ?: manifestFile - } - - try { - uOptions.downloadURI = SpaceSafeURI(unparsedArgs[0]) + val uOptions = try { + UpdateManager.Options.construct( + downloadURI = SpaceSafeURI(unparsedArgs[0]), + side = cmd.getOptionValue("side")?.let((UpdateManager.Options.Side)::from), + packFolder = cmd.getOptionValue("pack-folder"), + manifestFile = cmd.getOptionValue("meta-file") + ) } catch (e: URISyntaxException) { - // TODO: better error message? - ui.handleExceptionAndExit(e) + ui.showErrorAndExit("Failed to read pack.toml URI", e) } // Start update process! try { UpdateManager(uOptions, ui) - } catch (e: Exception) { // TODO: better error message? - ui.handleExceptionAndExit(e) + } catch (e: Exception) { + ui.showErrorAndExit("Update process failed", e) } println("Finished successfully!") ui.dispose() @@ -111,17 +114,17 @@ class Main(args: Array) { try { startup(args) } catch (e: Exception) { - e.printStackTrace() + Log.fatal("Error from main", e) if (guiEnabled) { EventQueue.invokeLater { JOptionPane.showMessageDialog(null, - "A fatal error occurred: \n" + e.javaClass.canonicalName + ": " + e.message, + "A fatal error occurred: \n$e", "packwiz-installer", JOptionPane.ERROR_MESSAGE) exitProcess(1) } + // In case the EventQueue is broken, exit after 1 minute + Thread.sleep(60 * 1000.toLong()) } - // In case the EventQueue is broken, exit after 1 minute - Thread.sleep(60 * 1000.toLong()) exitProcess(1) } } diff --git a/src/main/kotlin/link/infra/packwiz/installer/UpdateManager.kt b/src/main/kotlin/link/infra/packwiz/installer/UpdateManager.kt index ba0db0c..7cb9543 100644 --- a/src/main/kotlin/link/infra/packwiz/installer/UpdateManager.kt +++ b/src/main/kotlin/link/infra/packwiz/installer/UpdateManager.kt @@ -19,6 +19,9 @@ import link.infra.packwiz.installer.ui.IUserInterface import link.infra.packwiz.installer.ui.IUserInterface.CancellationResult import link.infra.packwiz.installer.ui.IUserInterface.ExceptionListResult import link.infra.packwiz.installer.ui.data.InstallProgress +import link.infra.packwiz.installer.util.Log +import link.infra.packwiz.installer.util.ifletOrErr +import link.infra.packwiz.installer.util.ifletOrWarn import okio.buffer import java.io.FileNotFoundException import java.io.FileReader @@ -43,11 +46,17 @@ class UpdateManager internal constructor(private val opts: Options, val ui: IUse } data class Options( - var downloadURI: SpaceSafeURI? = null, - var manifestFile: String = "packwiz.json", // TODO: make configurable - var packFolder: String = ".", - var side: Side = Side.CLIENT + val downloadURI: SpaceSafeURI, + val manifestFile: String, + val packFolder: String, + val side: Side ) { + // Horrible workaround for default params not working cleanly with nullable values + companion object { + fun construct(downloadURI: SpaceSafeURI, manifestFile: String?, packFolder: String?, side: Side?) = + Options(downloadURI, manifestFile ?: "packwiz.json", packFolder ?: ".", side ?: Side.CLIENT) + } + enum class Side { @SerializedName("client") CLIENT("client"), @@ -111,11 +120,9 @@ class UpdateManager internal constructor(private val opts: Options, val ui: IUse } catch (e: FileNotFoundException) { ManifestFile() } catch (e: JsonSyntaxException) { - ui.handleExceptionAndExit(e) - return + ui.showErrorAndExit("Invalid local manifest file, try deleting ${opts.manifestFile}", e) } catch (e: JsonIOException) { - ui.handleExceptionAndExit(e) - return + ui.showErrorAndExit("Failed to read local manifest file, try deleting ${opts.manifestFile}", e) } if (ui.cancelButtonPressed) { @@ -125,19 +132,16 @@ class UpdateManager internal constructor(private val opts: Options, val ui: IUse ui.submitProgress(InstallProgress("Loading pack file...")) val packFileSource = try { - val src = getFileSource(opts.downloadURI!!) + val src = getFileSource(opts.downloadURI) getHasher("sha256").getHashingSource(src) } catch (e: Exception) { - // TODO: run cancellation window? - ui.handleExceptionAndExit(e) - return + ui.showErrorAndExit("Failed to download pack.toml", e) } val pf = packFileSource.buffer().use { try { Toml().read(it.inputStream()).to(PackFile::class.java) } catch (e: IllegalStateException) { - ui.handleExceptionAndExit(e) - return + ui.showErrorAndExit("Failed to parse pack.toml", e) } } @@ -169,40 +173,41 @@ class UpdateManager internal constructor(private val opts: Options, val ui: IUse } } if (invalid) { - println("File $fileUri invalidated, marked for redownloading") + Log.info("File $fileUri invalidated, marked for redownloading") invalidatedUris.add(fileUri) } } if (manifest.packFileHash?.let { packFileSource.hashIsEqual(it) } == true && invalidatedUris.isEmpty()) { - println("Modpack is already up to date!") + Log.info("Modpack is already up to date!") // todo: --force? if (!ui.optionsButtonPressed) { return } } - println("Modpack name: " + pf.name) + Log.info("Modpack name: ${pf.name}") if (ui.cancelButtonPressed) { showCancellationDialog() handleCancellation() } try { - val index = pf.index!! - getNewLoc(opts.downloadURI, index.file)?.let { newLoc -> - index.hashFormat?.let { hashFormat -> - processIndex( - newLoc, - getHash(index.hashFormat!!, index.hash!!), - hashFormat, - manifest, - invalidatedUris - ) + ifletOrWarn(pf.index, "No index file found") { index -> + ui.ifletOrErr(index.hashFormat, index.hash, "Pack has no hash or hashFormat for index") { hashFormat, hash -> + ui.ifletOrErr(getNewLoc(opts.downloadURI, index.file), "Pack has invalid index file: " + index.file) { newLoc -> + processIndex( + newLoc, + getHash(hashFormat, hash), + hashFormat, + manifest, + invalidatedUris + ) + } } } } catch (e1: Exception) { - ui.handleExceptionAndExit(e1) + ui.showErrorAndExit("Failed to process index file", e1) } handleCancellation() @@ -221,8 +226,7 @@ class UpdateManager internal constructor(private val opts: Options, val ui: IUse try { FileWriter(Paths.get(opts.packFolder, opts.manifestFile).toString()).use { writer -> gson.toJson(manifest, writer) } } catch (e: IOException) { - // TODO: add message? - ui.handleException(e) + ui.showErrorAndExit("Failed to save local manifest file", e) } } @@ -232,7 +236,7 @@ class UpdateManager internal constructor(private val opts: Options, val ui: IUse private fun processIndex(indexUri: SpaceSafeURI, indexHash: Hash, hashFormat: String, manifest: ManifestFile, invalidatedUris: List) { if (manifest.indexFileHash == indexHash && invalidatedUris.isEmpty()) { - println("Modpack files are already up to date!") + Log.info("Modpack files are already up to date!") if (!ui.optionsButtonPressed) { return } @@ -243,20 +247,18 @@ class UpdateManager internal constructor(private val opts: Options, val ui: IUse val src = getFileSource(indexUri) getHasher(hashFormat).getHashingSource(src) } catch (e: Exception) { - // TODO: run cancellation window? - ui.handleExceptionAndExit(e) - return + ui.showErrorAndExit("Failed to download index file", e) } + val indexFile = try { Toml().read(indexFileSource.buffer().inputStream()).to(IndexFile::class.java) } catch (e: IllegalStateException) { - ui.handleExceptionAndExit(e) - return + ui.showErrorAndExit("Failed to parse index file", e) } if (!indexFileSource.hashIsEqual(indexHash)) { - ui.handleExceptionAndExit(RuntimeException("Your index hash is invalid! Please run packwiz refresh on the pack again")) - return + ui.showErrorAndExit("Your index file hash is invalid! The pack developer should packwiz refresh on the pack again") } + if (ui.cancelButtonPressed) { showCancellationDialog() return @@ -274,8 +276,7 @@ class UpdateManager internal constructor(private val opts: Options, val ui: IUse try { Files.deleteIfExists(Paths.get(opts.packFolder, file.cachedLocation)) } catch (e: IOException) { - // TODO: should this be shown to the user in some way? - e.printStackTrace() + Log.warn("Failed to delete optional disabled file", e) } // Set to null, as it doesn't exist anymore file.cachedLocation = null @@ -285,8 +286,8 @@ class UpdateManager internal constructor(private val opts: Options, val ui: IUse if (!alreadyDeleted) { try { Files.deleteIfExists(Paths.get(opts.packFolder, file.cachedLocation)) - } catch (e: IOException) { // TODO: should this be shown to the user in some way? - e.printStackTrace() + } catch (e: IOException) { + Log.warn("Failed to delete file removed from index", e) } } it.remove() @@ -302,14 +303,14 @@ class UpdateManager internal constructor(private val opts: Options, val ui: IUse // TODO: progress bar? if (indexFile.files.isEmpty()) { - println("Warning: Index is empty!") + Log.warn("Index is empty!") } val tasks = createTasksFromIndex(indexFile, indexFile.hashFormat, opts.side) // If the side changes, invalidate EVERYTHING just in case // Might not be needed, but done just to be safe val invalidateAll = opts.side != manifest.cachedSide if (invalidateAll) { - println("Side changed, invalidating all mods") + Log.info("Side changed, invalidating all mods") } tasks.forEach{ f -> // TODO: should linkedfile be checked as well? should this be done in the download section? @@ -377,18 +378,15 @@ class UpdateManager internal constructor(private val opts: Options, val ui: IUse } } for (i in tasks.indices) { - var task: DownloadTask? - task = try { + val task: DownloadTask = try { completionService.take().get() } catch (e: InterruptedException) { - ui.handleException(e) - null + ui.showErrorAndExit("Interrupted when consuming download tasks", e) } catch (e: ExecutionException) { - ui.handleException(e) - null + ui.showErrorAndExit("Failed to execute download task", e) } // Update manifest - If there were no errors cachedFile has already been modified in place (good old pass by reference) - task?.cachedFile?.let { file -> + task.cachedFile?.let { file -> if (task.failed()) { val oldFile = file.revert if (oldFile != null) { @@ -399,17 +397,11 @@ class UpdateManager internal constructor(private val opts: Options, val ui: IUse } } - var progress: String - if (task != null) { - val exDetails = task.exceptionDetails - if (exDetails != null) { - progress = "Failed to download ${exDetails.name}: ${exDetails.exception.message}" - exDetails.exception.printStackTrace() - } else { - progress = "Downloaded ${task.name}" - } + val exDetails = task.exceptionDetails + val progress = if (exDetails != null) { + "Failed to download ${exDetails.name}: ${exDetails.exception.message}" } else { - progress = "Failed to download, unknown reason" + "Downloaded ${task.name}" } ui.submitProgress(InstallProgress(progress, i + 1, tasks.size)) diff --git a/src/main/kotlin/link/infra/packwiz/installer/request/HandlerManager.kt b/src/main/kotlin/link/infra/packwiz/installer/request/HandlerManager.kt index 32b843a..1160e38 100644 --- a/src/main/kotlin/link/infra/packwiz/installer/request/HandlerManager.kt +++ b/src/main/kotlin/link/infra/packwiz/installer/request/HandlerManager.kt @@ -14,6 +14,7 @@ object HandlerManager { RequestHandlerFile() ) + // TODO: get rid of nullable stuff here @JvmStatic fun getNewLoc(base: SpaceSafeURI?, loc: SpaceSafeURI?): SpaceSafeURI? { if (loc == null) { @@ -32,6 +33,8 @@ object HandlerManager { // Zip handler discards once read, requesting multiple times on other handlers would cause multiple downloads // Caching system? Copy from already downloaded files? + // TODO: change to use something more idiomatic than exceptions? + @JvmStatic @Throws(Exception::class) fun getFileSource(loc: SpaceSafeURI): Source { diff --git a/src/main/kotlin/link/infra/packwiz/installer/ui/IUserInterface.kt b/src/main/kotlin/link/infra/packwiz/installer/ui/IUserInterface.kt index 8a690e0..b83864a 100644 --- a/src/main/kotlin/link/infra/packwiz/installer/ui/IUserInterface.kt +++ b/src/main/kotlin/link/infra/packwiz/installer/ui/IUserInterface.kt @@ -3,16 +3,15 @@ package link.infra.packwiz.installer.ui import link.infra.packwiz.installer.ui.data.ExceptionDetails import link.infra.packwiz.installer.ui.data.IOptionDetails import link.infra.packwiz.installer.ui.data.InstallProgress -import kotlin.system.exitProcess interface IUserInterface { fun show() fun dispose() - fun handleException(e: Exception) - fun handleExceptionAndExit(e: Exception) { - handleException(e) - exitProcess(1) + + fun showErrorAndExit(message: String): Nothing { + showErrorAndExit(message, null) } + fun showErrorAndExit(message: String, e: Exception?): Nothing fun setTitle(title: String) {} fun submitProgress(progress: InstallProgress) diff --git a/src/main/kotlin/link/infra/packwiz/installer/ui/cli/CLIHandler.kt b/src/main/kotlin/link/infra/packwiz/installer/ui/cli/CLIHandler.kt index 5e01b02..81a14bd 100644 --- a/src/main/kotlin/link/infra/packwiz/installer/ui/cli/CLIHandler.kt +++ b/src/main/kotlin/link/infra/packwiz/installer/ui/cli/CLIHandler.kt @@ -5,6 +5,7 @@ import link.infra.packwiz.installer.ui.IUserInterface.ExceptionListResult import link.infra.packwiz.installer.ui.data.ExceptionDetails import link.infra.packwiz.installer.ui.data.IOptionDetails import link.infra.packwiz.installer.ui.data.InstallProgress +import link.infra.packwiz.installer.util.Log import kotlin.system.exitProcess class CLIHandler : IUserInterface { @@ -13,8 +14,13 @@ class CLIHandler : IUserInterface { @Volatile override var cancelButtonPressed = false - override fun handleException(e: Exception) { - e.printStackTrace() + override fun showErrorAndExit(message: String, e: Exception?): Nothing { + if (e != null) { + Log.fatal(message, e) + } else { + Log.fatal(message) + } + exitProcess(1) } override fun show() {} @@ -36,7 +42,7 @@ class CLIHandler : IUserInterface { for (opt in options) { opt.optionValue = true // TODO: implement option choice in the CLI? - println("Warning: accepting option " + opt.name + " as option choosing is not implemented in the CLI") + Log.warn("Accepting option ${opt.name} as option choosing is not implemented in the CLI") } return false // Can't be cancelled! } @@ -44,9 +50,9 @@ class CLIHandler : IUserInterface { override fun showExceptions(exceptions: List, numTotal: Int, allowsIgnore: Boolean): ExceptionListResult { println("Failed to download modpack, the following errors were encountered:") for (ex in exceptions) { - println(ex.name + ": ") + print(ex.name + ": ") ex.exception.printStackTrace() } - exitProcess(1) + return ExceptionListResult.CANCEL } } \ No newline at end of file diff --git a/src/main/kotlin/link/infra/packwiz/installer/ui/gui/GUIHandler.kt b/src/main/kotlin/link/infra/packwiz/installer/ui/gui/GUIHandler.kt index 782dec5..e2cd51b 100644 --- a/src/main/kotlin/link/infra/packwiz/installer/ui/gui/GUIHandler.kt +++ b/src/main/kotlin/link/infra/packwiz/installer/ui/gui/GUIHandler.kt @@ -5,6 +5,7 @@ import link.infra.packwiz.installer.ui.IUserInterface.ExceptionListResult import link.infra.packwiz.installer.ui.data.ExceptionDetails import link.infra.packwiz.installer.ui.data.IOptionDetails import link.infra.packwiz.installer.ui.data.InstallProgress +import link.infra.packwiz.installer.util.Log import java.awt.EventQueue import java.util.concurrent.CompletableFuture import javax.swing.JDialog @@ -27,8 +28,7 @@ class GUIHandler : IUserInterface { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()) } catch (e: Exception) { - println("Failed to set look and feel:") - e.printStackTrace() + Log.warn("Failed to set look and feel", e) } frmPackwizlauncher = InstallWindow(this).apply { title = this@GUIHandler.title @@ -44,23 +44,23 @@ class GUIHandler : IUserInterface { frmPackwizlauncher.dispose() } - override fun handleException(e: Exception) { - e.printStackTrace() - EventQueue.invokeAndWait { - JOptionPane.showMessageDialog(null, - "An error occurred: \n" + e.javaClass.canonicalName + ": " + e.message, + override fun showErrorAndExit(message: String, e: Exception?): Nothing { + if (e != null) { + Log.fatal(message, e) + EventQueue.invokeAndWait { + JOptionPane.showMessageDialog(null, + "$message: $e", title, JOptionPane.ERROR_MESSAGE) - } - } - - override fun handleExceptionAndExit(e: Exception) { - e.printStackTrace() - EventQueue.invokeAndWait { - JOptionPane.showMessageDialog(null, - "A fatal error occurred: \n" + e.javaClass.canonicalName + ": " + e.message, + } + } else { + Log.fatal(message) + EventQueue.invokeAndWait { + JOptionPane.showMessageDialog(null, + message, title, JOptionPane.ERROR_MESSAGE) - exitProcess(1) + } } + exitProcess(1) } override fun setTitle(title: String) { @@ -78,8 +78,7 @@ class GUIHandler : IUserInterface { sb.append(") ") } sb.append(progress.message) - // TODO: better logging library? - println(sb.toString()) + Log.info(sb.toString()) EventQueue.invokeLater { frmPackwizlauncher.displayProgress(progress) } diff --git a/src/main/kotlin/link/infra/packwiz/installer/util/Exts.kt b/src/main/kotlin/link/infra/packwiz/installer/util/Exts.kt new file mode 100644 index 0000000..73ae30c --- /dev/null +++ b/src/main/kotlin/link/infra/packwiz/installer/util/Exts.kt @@ -0,0 +1,38 @@ +package link.infra.packwiz.installer.util + +import link.infra.packwiz.installer.ui.IUserInterface + +inline fun iflet(value: T?, whenNotNull: (T) -> Unit) { + if (value != null) { + whenNotNull(value) + } +} + +inline fun IUserInterface.ifletOrErr(value: T?, message: String, whenNotNull: (T) -> U): U = + if (value != null) { + whenNotNull(value) + } else { + this.showErrorAndExit(message) + } + +inline fun IUserInterface.ifletOrErr(value: T?, value2: U?, message: String, whenNotNull: (T, U) -> V): V = + if (value != null && value2 != null) { + whenNotNull(value, value2) + } else { + this.showErrorAndExit(message) + } + +inline fun ifletOrWarn(value: T?, message: String, whenNotNull: (T) -> Unit) { + if (value != null) { + whenNotNull(value) + } else { + Log.warn(message) + } +} + +inline fun iflet(value: T?, whenNotNull: (T) -> U, whenNull: () -> U): U = + if (value != null) { + whenNotNull(value) + } else { + whenNull() + } diff --git a/src/main/kotlin/link/infra/packwiz/installer/util/Log.kt b/src/main/kotlin/link/infra/packwiz/installer/util/Log.kt new file mode 100644 index 0000000..caa5a61 --- /dev/null +++ b/src/main/kotlin/link/infra/packwiz/installer/util/Log.kt @@ -0,0 +1,16 @@ +package link.infra.packwiz.installer.util + +object Log { + fun info(message: String) = println(message) + + fun warn(message: String) = println("[Warning] $message") + fun warn(message: String, exception: Exception) = println("[Warning] $message: $exception") + + fun fatal(message: String) { + println("[FATAL] $message") + } + fun fatal(message: String, exception: Exception) { + println("[FATAL] $message: ") + exception.printStackTrace() + } +} \ No newline at end of file