Skip to content

Commit

Permalink
Implement DSL builder for export profiles & error severity handling
Browse files Browse the repository at this point in the history
  • Loading branch information
juraj-hrivnak committed Dec 17, 2024
1 parent b5ee8e0 commit 0020441
Show file tree
Hide file tree
Showing 14 changed files with 220 additions and 108 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ package teksturepako.pakku.api.actions.errors

enum class ErrorSeverity
{
ERROR, WARNING, NOTICE;
FATAL, ERROR, WARNING, NOTICE;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ 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.export.Packaging.*
import teksturepako.pakku.api.actions.export.RuleContext.Finished
import teksturepako.pakku.api.actions.export.profiles.defaultProfiles
import teksturepako.pakku.api.data.ConfigFile
import teksturepako.pakku.api.data.Dirs.cacheDir
import teksturepako.pakku.api.data.LockFile
Expand All @@ -19,25 +21,28 @@ import teksturepako.pakku.api.platforms.Platform
import teksturepako.pakku.debug
import teksturepako.pakku.io.*
import java.nio.file.Path
import kotlin.collections.List
import kotlin.collections.Map
import kotlin.collections.associate
import kotlin.collections.filter
import kotlin.collections.filterNot
import kotlin.collections.filterNotNull
import kotlin.collections.flatMap
import kotlin.collections.fold
import kotlin.collections.isNotEmpty
import kotlin.collections.listOf
import kotlin.collections.map
import kotlin.collections.mapNotNull
import kotlin.collections.plus
import kotlin.collections.toMap
import kotlin.io.path.*
import kotlin.time.Duration
import kotlin.time.measureTimedValue
import com.github.michaelbull.result.fold as resultFold

suspend fun exportDefaultProfiles(
onError: suspend (profile: ExportProfile, error: ActionError) -> Unit,
onSuccess: suspend (profile: ExportProfile, path: Path, duration: Duration) -> Unit,
lockFile: LockFile,
configFile: ConfigFile,
platforms: List<Platform>
): List<Job>
{
return export(
profiles = defaultProfiles.map { it.build(exportingScope(lockFile, configFile)) },
onError = { profile, error -> onError(profile, error) },
onSuccess = { profile, path, duration -> onSuccess(profile, path, duration) },
lockFile, configFile, platforms
)
}

suspend fun export(
profiles: List<ExportProfile>,
Expand Down Expand Up @@ -81,7 +86,7 @@ suspend fun ExportProfile.export(
clientOverrides: Deferred<List<Result<String, ActionError>>>,
)
{
if (this.dependsOn != null && this.dependsOn !in platforms) return
if (this.requiresPlatform != null && this.requiresPlatform !in platforms) return

val timedValue = measureTimedValue {

Expand All @@ -105,7 +110,7 @@ suspend fun ExportProfile.export(
val inputDirectory = Path(cacheDir.pathString, this.name)

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

Expand Down Expand Up @@ -194,6 +199,7 @@ suspend fun List<RuleResult>.resolveResults(
{
onError(packagingAction.error)
debug { println(ruleResult) }
if (packagingAction.error.severity == ErrorSeverity.FATAL) return@coroutineScope listOf()
null
}
is Ignore -> null
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package teksturepako.pakku.api.actions.export

import teksturepako.pakku.api.data.ConfigFile
import teksturepako.pakku.api.data.LockFile
import teksturepako.pakku.api.platforms.Platform
import teksturepako.pakku.debug

/**
* An export profile is used to contain a list of [export rules][ExportRule].
Expand All @@ -17,5 +20,101 @@ open class ExportProfile(
val name: String,
val fileExtension: String = "zip",
val rules: List<ExportRule?>,
val dependsOn: Platform? = null
val requiresPlatform: Platform? = null
)

/**
* Creates an export profile with customized settings and export rules.
*
* ```
* val profile = exportProfile(name = "MyProfile") {
* rule { /* Add export rules */ }
* optionalRule { /* Add optional export rules */ } orElse rule { /* ... */ }
* }
* ```
*
* @param name The unique name for the export profile.
* @param fileExtension The file extension for the exported profile (defaults to "zip").
* @param requiresPlatform Optional platform constraint for the export profile.
* @param builder A lambda function to add export rules.
*/
fun exportProfile(
name: String,
fileExtension: String = "zip",
requiresPlatform: Platform? = null,
builder: ExportProfileBuilder.() -> Unit
): ExportProfileBuilder = ExportProfileBuilder(name, fileExtension, requiresPlatform, builder)

class ExportProfileBuilder(
private val name: String,
private val fileExtension: String = "zip",
private val requiresPlatform: Platform? = null,
private val builder: (ExportProfileBuilder.() -> Unit),
private var rules: Sequence<ExportRule> = sequenceOf(),
) : ExportingScope
{
override lateinit var lockFile: LockFile
override lateinit var configFile: ConfigFile

fun build(exportingScope: ExportingScope): ExportProfile
{
lockFile = exportingScope.lockFile
configFile = exportingScope.configFile

builder.invoke(this)

debug { println("Building $name profile") }

return ExportProfile(name, fileExtension, rules.toList(), requiresPlatform)
}

// -- AFTER BUILD --

/** Adds an export rule to the profile. */
fun rule(exportRule: (ExportingScope) -> ExportRule): ExportRule
{
val rule = exportRule(this)

rules += rule
return rule
}

/** Adds an optional export rule to the profile. */
fun optionalRule(exportRule: (ExportingScope) -> ExportRule?): ExportRule?
{
val rule = exportRule(this)

if (rule != null)
{
rules += rule
return rule
}
else return null
}

/** Provides a fallback mechanism when an optional rule is null. */
infix fun ExportRule?.orElse(exportRule: ExportRule): ExportRule?
{
if (this == null)
{
rules += exportRule
return exportRule
}
else return null
}
}

interface ExportingScope
{
/** The lock file associated with the exporting scope. */
val lockFile: LockFile

/** The config file associated with the exporting scope. */
val configFile: ConfigFile
}

fun exportingScope(lockFile: LockFile, configFile: ConfigFile): ExportingScope = object : ExportingScope
{
override val lockFile: LockFile = lockFile
override val configFile: ConfigFile = configFile
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ import kotlin.io.path.*
/**
* Rule Context keeps track of currently exporting content.
*/
sealed class RuleContext(open val workingSubDir: String)
sealed class RuleContext(
open val workingSubDir: String,
open val lockFile: LockFile,
open val configFile: ConfigFile
)
{
fun getPath(path: String, vararg subpath: String) =
Path(cacheDir.pathString, workingSubDir, path, *subpath)
Expand Down Expand Up @@ -91,10 +95,10 @@ sealed class RuleContext(open val workingSubDir: String)
/** Rule context representing a [project][Project]. */
data class ExportingProject(
val project: Project,
val lockFile: LockFile,
val configFile: ConfigFile,
override val lockFile: LockFile,
override val configFile: ConfigFile,
override val workingSubDir: String
) : RuleContext(workingSubDir)
) : RuleContext(workingSubDir, lockFile, configFile)
{
/** Sets the [project entry][RuleContext.ExportingProject] missing. */
fun setMissing(): RuleResult = MissingProject(
Expand Down Expand Up @@ -130,10 +134,10 @@ sealed class RuleContext(open val workingSubDir: String)
data class ExportingOverride(
val path: String,
val type: OverrideType,
val lockFile: LockFile,
val configFile: ConfigFile,
override val lockFile: LockFile,
override val configFile: ConfigFile,
override val workingSubDir: String
) : RuleContext(workingSubDir)
) : RuleContext(workingSubDir, lockFile, configFile)
{
fun export(overridesDir: String? = type.folderName): RuleResult
{
Expand Down Expand Up @@ -173,10 +177,10 @@ sealed class RuleContext(open val workingSubDir: String)
/** Rule context representing a [project override][ProjectOverride]. */
data class ExportingProjectOverride(
val projectOverride: ProjectOverride,
val lockFile: LockFile,
val configFile: ConfigFile,
override val lockFile: LockFile,
override val configFile: ConfigFile,
override val workingSubDir: String
) : RuleContext(workingSubDir)
) : RuleContext(workingSubDir, lockFile, configFile)
{
fun export(overridesDir: String? = projectOverride.type.folderName): RuleResult
{
Expand All @@ -203,10 +207,10 @@ sealed class RuleContext(open val workingSubDir: String)
/** Rule context representing a [missing project][Project]. */
data class MissingProject(
val project: Project,
val lockFile: LockFile,
val configFile: ConfigFile,
override val lockFile: LockFile,
override val configFile: ConfigFile,
override val workingSubDir: String
) : RuleContext(workingSubDir)
) : RuleContext(workingSubDir, lockFile, configFile)
{
suspend fun exportAsOverrideFrom(
provider: Provider,
Expand Down Expand Up @@ -263,10 +267,10 @@ sealed class RuleContext(open val workingSubDir: String)

/** Rule context indicating that all other actions have been finished. */
data class Finished(
val lockFile: LockFile,
val configFile: ConfigFile,
override val lockFile: LockFile,
override val configFile: ConfigFile,
override val workingSubDir: String
) : RuleContext(workingSubDir)
) : RuleContext(workingSubDir, lockFile, configFile)
{
fun replaceText(vararg pairs: Pair<String, String>): RuleResult
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,14 @@
package teksturepako.pakku.api.actions.export.profiles

import teksturepako.pakku.api.actions.export.ExportProfile
import teksturepako.pakku.api.actions.export.rules.createCfModpackModel
import teksturepako.pakku.api.actions.export.exportProfile
import teksturepako.pakku.api.actions.export.rules.cfMissingProjectsRule
import teksturepako.pakku.api.actions.export.rules.cfModpackRule
import teksturepako.pakku.api.actions.export.rules.replacementRule
import teksturepako.pakku.api.actions.export.rules.ruleOfCfMissingProjects
import teksturepako.pakku.api.actions.export.rules.ruleOfCfModpack
import teksturepako.pakku.api.data.ConfigFile
import teksturepako.pakku.api.data.LockFile
import teksturepako.pakku.api.platforms.CurseForge
import teksturepako.pakku.compat.exportFileDirector
import teksturepako.pakku.compat.fileDirectorRule

class CurseForgeProfile(lockFile: LockFile, configFile: ConfigFile) : ExportProfile(
name = CurseForge.serialName,
rules = listOf(
lockFile.getFirstMcVersion()?.let {
val modpackModel = createCfModpackModel(it, lockFile, configFile)
ruleOfCfModpack(modpackModel)
},
if (lockFile.getAllProjects().any { "filedirector" in it }) exportFileDirector(excludedProviders = setOf(CurseForge))
else ruleOfCfMissingProjects(),
replacementRule()
),
dependsOn = CurseForge
)
fun curseForgeProfile() = exportProfile(name = CurseForge.serialName, requiresPlatform = CurseForge) {
rule { cfModpackRule() }
optionalRule { fileDirectorRule(excludedProviders = setOf(CurseForge)) } orElse rule { cfMissingProjectsRule() }
rule { replacementRule() }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package teksturepako.pakku.api.actions.export.profiles

val defaultProfiles = listOf(
curseForgeProfile(),
modrinthProfile(),
serverPackProfile()
)
Original file line number Diff line number Diff line change
@@ -1,26 +1,16 @@
package teksturepako.pakku.api.actions.export.profiles

import teksturepako.pakku.api.actions.export.ExportProfile
import teksturepako.pakku.api.actions.export.rules.createMrModpackModel
import teksturepako.pakku.api.actions.export.exportProfile
import teksturepako.pakku.api.actions.export.rules.mrMissingProjectsRule
import teksturepako.pakku.api.actions.export.rules.mrModpackRule
import teksturepako.pakku.api.actions.export.rules.replacementRule
import teksturepako.pakku.api.actions.export.rules.ruleOfMrMissingProjects
import teksturepako.pakku.api.actions.export.rules.ruleOfMrModpack
import teksturepako.pakku.api.data.ConfigFile
import teksturepako.pakku.api.data.LockFile
import teksturepako.pakku.api.platforms.Modrinth
import teksturepako.pakku.compat.exportFileDirector
import teksturepako.pakku.compat.fileDirectorRule

class ModrinthProfile(lockFile: LockFile, configFile: ConfigFile) : ExportProfile(
name = Modrinth.serialName,
fileExtension = "mrpack",
rules = listOf(
lockFile.getFirstMcVersion()?.let {
val modpackModel = createMrModpackModel(it, lockFile, configFile)
ruleOfMrModpack(modpackModel)
},
if (lockFile.getAllProjects().any { "filedirector" in it }) exportFileDirector(excludedProviders = setOf(Modrinth))
else ruleOfMrMissingProjects(),
replacementRule()
),
dependsOn = Modrinth
)
fun modrinthProfile() = exportProfile(
name = Modrinth.serialName, fileExtension = "mrpack", requiresPlatform = Modrinth
) {
rule { mrModpackRule() }
optionalRule { fileDirectorRule(excludedProviders = setOf(Modrinth)) } orElse rule { mrMissingProjectsRule() }
rule { replacementRule() }
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
package teksturepako.pakku.api.actions.export.profiles

import teksturepako.pakku.api.actions.export.ExportProfile
import teksturepako.pakku.api.actions.export.rules.exportServerPack
import teksturepako.pakku.api.actions.export.exportProfile
import teksturepako.pakku.api.actions.export.rules.replacementRule
import teksturepako.pakku.api.actions.export.rules.serverPackRule

class ServerPackProfile : ExportProfile(
name = "serverpack",
rules = listOf(
exportServerPack(),
replacementRule()
)
)
fun serverPackProfile() = exportProfile(name = "serverpack") {
rule { serverPackRule() }
rule { replacementRule() }
}
Loading

0 comments on commit 0020441

Please sign in to comment.