Skip to content

Commit

Permalink
Implement better error handling for export action
Browse files Browse the repository at this point in the history
  • Loading branch information
juraj-hrivnak committed Feb 2, 2025
1 parent 132ec16 commit fb39d69
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ abstract class ActionError

/** A message dedicated for the CLI. It should not be used outside of terminal. */
open fun message(arg: String = ""): String = rawMessage

protected fun message(vararg args: String): String = args.joinToString("")
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

package teksturepako.pakku.api.actions.errors

import teksturepako.pakku.api.actions.export.ExportProfile
import teksturepako.pakku.api.data.LockFile
import teksturepako.pakku.api.platforms.Provider
import teksturepako.pakku.api.projects.Project
Expand Down Expand Up @@ -119,6 +120,29 @@ data class NotRedistributable(val project: Project) : ActionError()
"${dim(project.type)} ${project.getFlavoredSlug()} can not be exported, because it is not redistributable."
}

data class CouldNotExport(val profile: ExportProfile, val modpackFileName: String, val reason: String?) : ActionError()
{
override val rawMessage = "Profile ${profile.name} ('$modpackFileName') could not be exported. $reason"
override val severity = ErrorSeverity.FATAL
}

data class ErrorWhileExporting(
val profile: ExportProfile, val modpackFileName: String, val underlyingError: ActionError
) : ActionError()
{
override val rawMessage = message(
"There was an error while exporting profile ${profile.name} ('$modpackFileName'). ",
underlyingError.rawMessage
)

override fun message(arg: String): String = message(
"There was an error while exporting profile ${profile.name} ('$modpackFileName'). ",
underlyingError.message()
)

override val severity = underlyingError.severity
}

// -- ADDITION --

data class AlreadyAdded(val project: Project) : ActionError()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ import com.github.michaelbull.result.get
import com.github.michaelbull.result.onFailure
import com.github.michaelbull.result.runCatching
import kotlinx.coroutines.*
import teksturepako.pakku.api.actions.errors.ActionError
import teksturepako.pakku.api.actions.errors.CouldNotSave
import teksturepako.pakku.api.actions.errors.ErrorSeverity
import teksturepako.pakku.api.actions.errors.*
import teksturepako.pakku.api.actions.export.Packaging.*
import teksturepako.pakku.api.actions.export.RuleContext.Finished
import teksturepako.pakku.api.actions.export.profiles.defaultProfiles
Expand All @@ -20,6 +18,7 @@ import teksturepako.pakku.api.overrides.readProjectOverrides
import teksturepako.pakku.api.platforms.Platform
import teksturepako.pakku.debug
import teksturepako.pakku.io.*
import java.lang.Exception
import java.nio.file.Path
import kotlin.collections.fold
import kotlin.collections.map
Expand Down Expand Up @@ -98,8 +97,10 @@ suspend fun ExportProfile.export(
} + ".${this.fileExtension}"

val outputZipFile = runCatching { Path(workingPath, "build", this.name, modpackFileName) }
.onFailure { onError(this, CouldNotSave(null, it.message)) }
.get() ?: return
.onFailure { e: Throwable ->
onError(this, CouldNotExport(this, modpackFileName, e.message))
}
.get() ?: return@measureTimedValue null

// Create parent directory
cacheDir.tryOrNull {
Expand All @@ -110,21 +111,35 @@ suspend fun ExportProfile.export(
val inputDirectory = Path(cacheDir.pathString, this.name)

val results: List<RuleResult> = this.rules.filterNotNull().produceRuleResults(
onError = { error -> onError(this, error) },
onError = { error -> onError(this, ErrorWhileExporting(this, modpackFileName, error)) },
lockFile, configFile, this.name, overrides, serverOverrides, clientOverrides
)

// Run export rules
val cachedPaths: List<Path> = results.resolveResults { onError(this, it) }.awaitAll().filterNotNull() +
results.finishResults { onError(this, it) }.awaitAll().filterNotNull()
val cachedPaths: List<Path> = results
.resolveResults { error ->
onError(this, ErrorWhileExporting(this, modpackFileName, error))
}
.awaitAll()
.filterNotNull()
.plus(
results
.finishResults { error ->
onError(this, ErrorWhileExporting(this, modpackFileName, error))
}
.awaitAll()
.filterNotNull()
)

// -- FILE CACHE --

/** Map of _absolute paths_ to their _hashes_ for every path not in a directory */
val fileHashes: Map<Path, String> = cachedPaths.filterNot { it.isDirectory() }
.mapNotNull { file ->
file.tryToResult { it.readBytes() }
.onFailure { onError(this, it) }
.onFailure { error ->
onError(this, ErrorWhileExporting(this, modpackFileName, error))
}
.get()?.let { file to it }
}
.associate { it.first.absolute() to createHash("sha1", it.second) }
Expand All @@ -133,7 +148,9 @@ suspend fun ExportProfile.export(
val dirContentHashes: Map<Path, String> = cachedPaths.filter { it.isDirectory() }
.mapNotNull { directory ->
directory.tryToResult { it.toFile().walkTopDown() }
.onFailure { onError(this, it) }.get()
.onFailure { error ->
onError(this, ErrorWhileExporting(this, modpackFileName, error))
}.get()
}
.flatMap {
it.mapNotNull { file -> file.toPath() }
Expand All @@ -146,7 +163,9 @@ suspend fun ExportProfile.export(
.toMap()

val fileTreeWalk = inputDirectory.tryToResult { it.toFile().walkBottomUp() }
.onFailure { onError(this, it) }.get()
.onFailure {error ->
onError(this, ErrorWhileExporting(this, modpackFileName, error))
}.get()

if (fileTreeWalk != null)
{
Expand All @@ -158,7 +177,9 @@ suspend fun ExportProfile.export(
if (path.isDirectory())
{
val currentDirFiles = path.tryToResult { it.toFile().listFiles() }
.onFailure { onError(this, it) }.get()?.mapNotNull { it.toPath() } ?: continue
.onFailure { error ->
onError(this, ErrorWhileExporting(this, modpackFileName, error))
}.get()?.mapNotNull { it.toPath() } ?: continue

if (currentDirFiles.isNotEmpty()) continue

Expand All @@ -179,14 +200,33 @@ suspend fun ExportProfile.export(

// -- ZIP CREATION --

outputZipFile.tryToResult { it.createParentDirectories() }.onFailure { onError(this, it) }
outputZipFile
.tryToResult { it.createParentDirectories() }
.onFailure { error ->
if (error !is AlreadyExists)
{
onError(this, CouldNotExport(this, modpackFileName, error.rawMessage))
return@measureTimedValue null
}
}

zip(inputDirectory, outputZipFile)
try
{
zip(inputDirectory, outputZipFile)
}
catch (e: Exception)
{
onError(this, CouldNotExport(this, modpackFileName, e.stackTraceToString()))
return@measureTimedValue null
}

outputZipFile
}

onSuccess(this, timedValue.value, timedValue.duration)
if (timedValue.value != null)
{
onSuccess(this, timedValue.value!!, timedValue.duration)
}
}

suspend fun List<RuleResult>.resolveResults(
Expand Down
4 changes: 2 additions & 2 deletions src/commonMain/kotlin/teksturepako/pakku/cli/cmd/Export.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import com.github.michaelbull.result.getOrElse
import kotlinx.coroutines.joinAll
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import teksturepako.pakku.api.actions.errors.AlreadyExists
import teksturepako.pakku.api.actions.errors.ErrorWhileExporting
import teksturepako.pakku.api.actions.export.exportDefaultProfiles
import teksturepako.pakku.api.data.ConfigFile
import teksturepako.pakku.api.data.LockFile
Expand Down Expand Up @@ -54,7 +54,7 @@ class Export : CliktCommand()

exportDefaultProfiles(
onError = { profile, error ->
if (error !is AlreadyExists)
if (error !is ErrorWhileExporting)
{
terminal.pError(error, prepend = "[${profile.name} profile]")
}
Expand Down

0 comments on commit fb39d69

Please sign in to comment.