[WIP] Config parser
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package mods.betterfoliage
|
||||
|
||||
import mods.betterfoliage.config.BlockConfig
|
||||
import mods.betterfoliage.config.BlockConfigOld
|
||||
import mods.betterfoliage.config.MainConfig
|
||||
import mods.betterfoliage.util.tryDefault
|
||||
import mods.betterfoliage.config.clothGuiRoot
|
||||
@@ -72,7 +73,7 @@ object BetterFoliageMod {
|
||||
}
|
||||
|
||||
Minecraft.getInstance().resourcePackRepository.addPackFinder(BetterFoliage.generatedPack.finder)
|
||||
bus.register(BlockConfig)
|
||||
bus.register(BlockConfigOld)
|
||||
BetterFoliage.init()
|
||||
}
|
||||
}
|
||||
40
src/main/kotlin/mods/betterfoliage/config/BlockConfig.kt
Normal file
40
src/main/kotlin/mods/betterfoliage/config/BlockConfig.kt
Normal file
@@ -0,0 +1,40 @@
|
||||
package mods.betterfoliage.config
|
||||
|
||||
import mods.betterfoliage.config.match.Node
|
||||
import mods.betterfoliage.config.match.parser.BlockConfigParser
|
||||
import mods.betterfoliage.config.match.parser.ParseException
|
||||
import mods.betterfoliage.config.match.parser.TokenMgrError
|
||||
import mods.betterfoliage.util.HasLogger
|
||||
import mods.betterfoliage.util.stripStart
|
||||
import net.minecraft.resources.IResourceManager
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import org.apache.logging.log4j.Level
|
||||
import org.apache.logging.log4j.Level.ERROR
|
||||
|
||||
class BlockConfig : HasLogger() {
|
||||
lateinit var rules: List<Node.MatchAll>
|
||||
|
||||
fun readConfig(manager: IResourceManager) {
|
||||
val configs = manager.listResources("config/betterfoliage") { it.endsWith(".rules") }
|
||||
rules = configs.flatMap { configLocation ->
|
||||
val resource = manager.getResource(configLocation)
|
||||
val parser = BlockConfigParser(resource.inputStream)
|
||||
.apply { configFile = configLocation.stripStart("config/betterfoliage/").toString() }
|
||||
try {
|
||||
mutableListOf<Node.MatchAll>().apply { parser.matchFile(this) }
|
||||
} catch (e: ParseException) {
|
||||
parseError(e, configLocation)
|
||||
} catch (e: TokenMgrError) {
|
||||
parseError(e, configLocation)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun parseError(e: Throwable, location: ResourceLocation): List<Node.MatchAll> {
|
||||
"Error parsing block config $location: ${e.message}".let {
|
||||
logger.log(ERROR, it)
|
||||
detailLogger.log(ERROR, it)
|
||||
}
|
||||
return emptyList()
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import net.minecraft.util.ResourceLocation
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent
|
||||
import net.minecraftforge.fml.config.ModConfig
|
||||
|
||||
object BlockConfig {
|
||||
object BlockConfigOld {
|
||||
private val list = mutableListOf<Any>()
|
||||
|
||||
val leafBlocks = blocks("leaves_blocks_default.cfg")
|
||||
|
||||
117
src/main/kotlin/mods/betterfoliage/config/match/Match.kt
Normal file
117
src/main/kotlin/mods/betterfoliage/config/match/Match.kt
Normal file
@@ -0,0 +1,117 @@
|
||||
package mods.betterfoliage.config.match
|
||||
|
||||
typealias RuleLogConsumer = (ConfigSource, String) -> Unit
|
||||
|
||||
sealed class MatchValue(val description: String) {
|
||||
class Found<T>(description: String, val value: T) : MatchValue(description)
|
||||
class Missing(description: String) : MatchValue(description)
|
||||
class Invalid(description: String) : MatchValue(description)
|
||||
}
|
||||
|
||||
sealed class MatchResult {
|
||||
abstract val isSuccess: Boolean
|
||||
abstract val isInvariant: Boolean
|
||||
abstract val configSource: ConfigSource
|
||||
abstract fun log(logger: RuleLogConsumer)
|
||||
|
||||
class UniComparison(
|
||||
override val isSuccess: Boolean,
|
||||
override val isInvariant: Boolean,
|
||||
override val configSource: ConfigSource,
|
||||
val sourceValue: MatchValue,
|
||||
val targetValue: String,
|
||||
val matchMethod: MatchMethod
|
||||
) : MatchResult() {
|
||||
override fun log(logger: RuleLogConsumer) = logger(
|
||||
configSource,
|
||||
"${sourceValue.description} ${matchMethod.description(isSuccess)} \"$targetValue\""
|
||||
)
|
||||
}
|
||||
|
||||
class BiComparison(
|
||||
override val isSuccess: Boolean,
|
||||
override val isInvariant: Boolean,
|
||||
override val configSource: ConfigSource,
|
||||
val source: MatchValue,
|
||||
val target: MatchValue,
|
||||
val matchMethod: MatchMethod
|
||||
) : MatchResult() {
|
||||
override fun log(logger: RuleLogConsumer) = logger(
|
||||
configSource,
|
||||
"${source.description} ${matchMethod.description(isSuccess)} ${target.description}"
|
||||
)
|
||||
}
|
||||
|
||||
class InvalidValue(override val configSource: ConfigSource, val value: MatchValue, val description: String) : MatchResult() {
|
||||
override val isSuccess = false
|
||||
override val isInvariant = true
|
||||
|
||||
override fun log(logger: RuleLogConsumer) = logger(configSource, description)
|
||||
}
|
||||
|
||||
class Error(override val configSource: ConfigSource, val description: String) : MatchResult() {
|
||||
override val isSuccess = false
|
||||
override val isInvariant = true
|
||||
|
||||
override fun log(logger: RuleLogConsumer) = logger(configSource, description)
|
||||
}
|
||||
|
||||
class Action(override val configSource: ConfigSource, val description: String) : MatchResult() {
|
||||
override val isSuccess = true
|
||||
override val isInvariant = false
|
||||
|
||||
override fun log(logger: RuleLogConsumer) = logger(configSource, description)
|
||||
}
|
||||
|
||||
class Any(override val configSource: ConfigSource, val results: List<MatchResult>) : MatchResult() {
|
||||
override val isSuccess = results.any(MatchResult::isSuccess)
|
||||
override val isInvariant = results.all(MatchResult::isInvariant)
|
||||
|
||||
override fun log(logger: RuleLogConsumer) {
|
||||
val toLog = if (results.any { it.isSuccess }) results.filter { it.isSuccess } else results
|
||||
toLog.forEach { it.log(logger) }
|
||||
}
|
||||
}
|
||||
|
||||
class RootList(override val configSource: ConfigSource, val results: List<MatchResult>) : MatchResult() {
|
||||
override val isSuccess = results.all(MatchResult::isSuccess)
|
||||
override val isInvariant = results.all(MatchResult::isInvariant)
|
||||
|
||||
override fun log(logger: RuleLogConsumer) {
|
||||
results.forEach { it.log(logger) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Node.HasSource.error(description: String) = MatchResult.Error(configSource, description)
|
||||
fun Node.HasSource.notImplemented() = MatchResult.Error(configSource, "match type not implemented: ${this::class.java.name}")
|
||||
fun Node.HasSource.action(description: String) = MatchResult.Action(configSource, description)
|
||||
fun Node.HasSource.invalidValue(value: MatchValue) = MatchResult.InvalidValue(
|
||||
configSource, value, "invalid value: ${value.description}"
|
||||
)
|
||||
fun Node.HasSource.invalidValueType(comparisonLeft: String, value: Node.Value) = MatchResult.Error(
|
||||
configSource, "invalid type for $comparisonLeft: [${value::class.java.name}] \"${value.value}\""
|
||||
)
|
||||
fun <T> Node.MatchValueList.compare(sourceValue: MatchValue, targetValue: Node.Value, func: (MatchValue.Found<T>) -> Boolean): MatchResult {
|
||||
if (sourceValue is MatchValue.Missing || sourceValue is MatchValue.Invalid) return invalidValue(sourceValue)
|
||||
val isSuccess = func(sourceValue as MatchValue.Found<T>)
|
||||
return MatchResult.UniComparison(isSuccess, true, configSource, sourceValue, targetValue.value, matchMethod)
|
||||
}
|
||||
fun <T> Node.MatchValueList.compare(sourceValue: MatchValue, targetValue: MatchValue, func: (MatchValue.Found<T>, MatchValue.Found<T>) -> Boolean): MatchResult {
|
||||
if (sourceValue is MatchValue.Missing || sourceValue is MatchValue.Invalid) return invalidValue(sourceValue)
|
||||
if (targetValue is MatchValue.Missing || targetValue is MatchValue.Invalid) return invalidValue(targetValue)
|
||||
val isSuccess = func(sourceValue as MatchValue.Found<T>, targetValue as MatchValue.Found<T>)
|
||||
return MatchResult.BiComparison(isSuccess, true, configSource, sourceValue, targetValue, matchMethod)
|
||||
}
|
||||
|
||||
enum class MatchMethod {
|
||||
EXACT_MATCH, EXTENDS, CONTAINS;
|
||||
|
||||
fun description(isSuccess: Boolean) = when(this) {
|
||||
EXACT_MATCH -> if (isSuccess) "matches" else "does not match"
|
||||
EXTENDS -> if (isSuccess) "extends" else "does not extend"
|
||||
CONTAINS -> if (isSuccess) "contains" else "does not contain"
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
150
src/main/kotlin/mods/betterfoliage/config/match/Matchers.kt
Normal file
150
src/main/kotlin/mods/betterfoliage/config/match/Matchers.kt
Normal file
@@ -0,0 +1,150 @@
|
||||
package mods.betterfoliage.config.match
|
||||
|
||||
import mods.betterfoliage.resource.discovery.RuleProcessingContext
|
||||
import mods.betterfoliage.resource.discovery.getAncestry
|
||||
import mods.betterfoliage.util.tryDefault
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.client.renderer.model.BlockModel
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import net.minecraftforge.registries.ForgeRegistries
|
||||
|
||||
object MatchRuleList {
|
||||
fun visitRoot(ctx: RuleProcessingContext, node: Node.MatchAll): MatchResult {
|
||||
val results = mutableListOf<MatchResult>()
|
||||
for (rule in node.list) {
|
||||
val result = when(rule) {
|
||||
is Node.MatchValueList -> visitMatchList(ctx, rule)
|
||||
is Node.MatchParam -> ParamMatchRules.visitMatch(ctx, rule)
|
||||
is Node.SetParam -> ParamMatchRules.visitSet(ctx, rule)
|
||||
else -> node.notImplemented()
|
||||
}
|
||||
results.add(result)
|
||||
if (!result.isSuccess) break
|
||||
}
|
||||
return MatchResult.RootList(node.configSource, results)
|
||||
}
|
||||
|
||||
fun visitMatchList(ctx: RuleProcessingContext, node: Node.MatchValueList) = node.values.map { value ->
|
||||
try {
|
||||
when (node.matchSource) {
|
||||
Node.MatchSource.BLOCK_CLASS -> BlockMatchRules.visitClass(ctx, node, value)
|
||||
Node.MatchSource.BLOCK_NAME -> BlockMatchRules.visitName(ctx, node, value)
|
||||
Node.MatchSource.MODEL_LOCATION -> ModelMatchRules.visit(ctx, node, value)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
MatchResult.Error(node.configSource, e.message ?: "")
|
||||
}
|
||||
}.let { MatchResult.Any(node.configSource, it) }
|
||||
}
|
||||
|
||||
object BlockMatchRules {
|
||||
fun visitClass(ctx: RuleProcessingContext, node: Node.MatchValueList, value: Node.Value): MatchResult {
|
||||
val source = ctx.discovery.blockState.block::class.java.let {
|
||||
MatchValue.Found("block class \"${it.name}\"", it)
|
||||
}
|
||||
val target = when(value) {
|
||||
is Node.Value.Literal -> tryDefault(null) { Class.forName(value.value) as Class<out Block> }
|
||||
?.let { MatchValue.Found("class \"${value.value}\"", it) }
|
||||
?: MatchValue.Missing("missing class \"${value.value}\"")
|
||||
|
||||
is Node.Value.ClassOf -> ForgeRegistries.BLOCKS.getValue(ResourceLocation(value.value))
|
||||
?.let { MatchValue.Found("class \"${it::class.java}\" of block \"${value.value}\"", it::class.java) }
|
||||
?: MatchValue.Missing("class of missing block \"${value.value}\"")
|
||||
|
||||
else -> MatchValue.Invalid("${value::class.java.name}(${value.value})")
|
||||
}
|
||||
return when(node.matchMethod) {
|
||||
MatchMethod.EXACT_MATCH -> node.compare(source, target, this::isExactClass)
|
||||
MatchMethod.EXTENDS -> node.compare(source, target, this::isExtendsClass)
|
||||
MatchMethod.CONTAINS -> node.error("invalid match type for block class: \"contains\"")
|
||||
}
|
||||
}
|
||||
|
||||
fun visitName(ctx: RuleProcessingContext, node: Node.MatchValueList, value: Node.Value): MatchResult {
|
||||
val source = ctx.discovery.blockState.block.registryName?.let {
|
||||
MatchValue.Found("block name \"$it\"", it)
|
||||
} ?: MatchValue.Missing("missing block name")
|
||||
if (value !is Node.Value.Literal) return node.invalidValueType("block name", value)
|
||||
val (namespace, path) = if (value.value.contains(":")) ResourceLocation(value.value).let { it.namespace to it.path } else null to value.value
|
||||
return when(node.matchMethod) {
|
||||
MatchMethod.EXACT_MATCH -> node.compare<ResourceLocation>(source, value) { isExactName(it.value, namespace, path) }
|
||||
MatchMethod.CONTAINS -> node.compare<ResourceLocation>(source, value) { isContainsName(it.value, namespace, path) }
|
||||
MatchMethod.EXTENDS -> node.error("invalid match type for block name: \"extends\"")
|
||||
}
|
||||
}
|
||||
|
||||
private fun isExactClass(source: MatchValue.Found<Class<out Block>>, target: MatchValue.Found<Class<out Block>>) =
|
||||
source.value == target.value
|
||||
private fun isExtendsClass(source: MatchValue.Found<Class<out Block>>, target: MatchValue.Found<Class<out Block>>) =
|
||||
target.value.isAssignableFrom(source.value)
|
||||
|
||||
fun isExactName(source: ResourceLocation, namespace: String?, path: String) =
|
||||
(namespace == null || namespace == source.namespace) && path == source.path
|
||||
fun isContainsName(source: ResourceLocation, namespace: String?, path: String) =
|
||||
(namespace == null || source.namespace.contains(namespace)) && source.path.contains(path)
|
||||
}
|
||||
|
||||
object ModelMatchRules {
|
||||
fun visit(ctx: RuleProcessingContext, node: Node.MatchValueList, value: Node.Value): MatchResult {
|
||||
val source = ctx.discovery.modelLocation.let { MatchValue.Found("model \"$it\"", it) }
|
||||
if (value !is Node.Value.Literal) return node.invalidValueType("model", value)
|
||||
val (namespace, path) = value.value.splitLocation()
|
||||
|
||||
val check = when (node.matchMethod) {
|
||||
MatchMethod.EXACT_MATCH, MatchMethod.CONTAINS -> listOf(ctx.discovery.modelLocation)
|
||||
MatchMethod.EXTENDS -> ctx.discovery.bakery.getAncestry(ctx.discovery.modelLocation)
|
||||
}
|
||||
|
||||
return when (node.matchMethod) {
|
||||
MatchMethod.EXACT_MATCH, MatchMethod.EXTENDS -> node.compare<ResourceLocation>(source, value) { isExactModel(check, namespace, path) }
|
||||
MatchMethod.CONTAINS -> node.compare<ResourceLocation>(source, value) { isContainsModel(check, namespace, path) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.splitLocation() = when(contains(":")) {
|
||||
true -> ResourceLocation(this).let { it.namespace to it.path }
|
||||
false -> null to this
|
||||
}
|
||||
|
||||
private fun isExactModel(models: List<ResourceLocation>, namespace: String?, path: String) = models.any { model ->
|
||||
(namespace == null || namespace == model.namespace) && path == model.path
|
||||
}
|
||||
|
||||
private fun isContainsModel(models: List<ResourceLocation>, namespace: String?, path: String) = models.any { model ->
|
||||
(namespace == null || model.namespace.contains(namespace)) && model.path.contains(path)
|
||||
}
|
||||
}
|
||||
|
||||
object ParamMatchRules {
|
||||
fun visitMatch(ctx: RuleProcessingContext, node: Node.MatchParam) = node.values.map { value ->
|
||||
if (value !is Node.Value.Literal) return@map node.invalidValueType("parameter", value)
|
||||
val currentParamValue = ctx.params[node.name] ?: return@map MatchResult.UniComparison(
|
||||
isSuccess = false, isInvariant = false,
|
||||
node.configSource,
|
||||
MatchValue.Missing("missing parameter \"${node.name}\""), value.value,
|
||||
MatchMethod.EXACT_MATCH
|
||||
)
|
||||
val isSuccess = currentParamValue == value.value
|
||||
MatchResult.UniComparison(
|
||||
isSuccess, false,
|
||||
node.configSource,
|
||||
MatchValue.Found("parameter \"${node.name}\"", currentParamValue), value.value,
|
||||
MatchMethod.EXACT_MATCH
|
||||
)
|
||||
}.let { MatchResult.Any(node.configSource, it) }
|
||||
|
||||
fun visitSet(ctx: RuleProcessingContext, node: Node.SetParam): MatchResult {
|
||||
val target = when(node.value) {
|
||||
is Node.Value.Literal -> node.value.value.let { MatchValue.Found("\"$it\"", it) }
|
||||
is Node.Value.Texture -> when(val model = ctx.discovery.getUnbaked()) {
|
||||
is BlockModel -> model.getMaterial(node.value.value).texture().toString().let {
|
||||
MatchValue.Found("texture \"${node.value.value}\" = \"$it\"", it)
|
||||
}
|
||||
else -> return node.error("cannot get texture from ${model::class.java.name}")
|
||||
}
|
||||
else -> return node.invalidValueType("parameter", node.value)
|
||||
}
|
||||
ctx.params[node.name] = target.value
|
||||
return node.action("parameter \"${node.name}\" set to ${target.description}")
|
||||
}
|
||||
}
|
||||
37
src/main/kotlin/mods/betterfoliage/config/match/ParseTree.kt
Normal file
37
src/main/kotlin/mods/betterfoliage/config/match/ParseTree.kt
Normal file
@@ -0,0 +1,37 @@
|
||||
package mods.betterfoliage.config.match
|
||||
|
||||
data class ConfigSource(
|
||||
val configFile: String,
|
||||
val line: Int,
|
||||
val column: Int
|
||||
) {
|
||||
override fun toString() = "$configFile @ R$line,C$column"
|
||||
}
|
||||
|
||||
sealed class Node {
|
||||
enum class MatchSource { BLOCK_CLASS, BLOCK_NAME, MODEL_LOCATION }
|
||||
interface HasSource { val configSource: ConfigSource }
|
||||
|
||||
class MatchValueList(
|
||||
val matchSource: MatchSource,
|
||||
val matchMethod: MatchMethod,
|
||||
override val configSource: ConfigSource,
|
||||
val values: List<Value>
|
||||
) : Node(), HasSource
|
||||
|
||||
class MatchParam(
|
||||
val name: String,
|
||||
val values: List<Value>,
|
||||
override val configSource: ConfigSource,
|
||||
) : Node(), HasSource
|
||||
|
||||
class SetParam(val name: String, val value: Value, override val configSource: ConfigSource) : Node(), HasSource
|
||||
|
||||
class MatchAll(override val configSource: ConfigSource, val list: List<Node>) : Node(), HasSource
|
||||
|
||||
abstract class Value(val value: String) : Node() {
|
||||
class Literal(value: String) : Value(value)
|
||||
class ClassOf(value: String) : Value(value)
|
||||
class Texture(value: String) : Value(value)
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,10 @@ abstract class AbstractModelDiscovery : HasLogger(), ModelDiscovery {
|
||||
}
|
||||
|
||||
open fun processModel(ctx: ModelDiscoveryContext) {
|
||||
processContainerModel(ctx)
|
||||
}
|
||||
|
||||
fun processContainerModel(ctx: ModelDiscoveryContext) {
|
||||
val model = ctx.getUnbaked()
|
||||
|
||||
// built-in support for container models
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
package mods.betterfoliage.resource.discovery
|
||||
|
||||
import mods.betterfoliage.BetterFoliage
|
||||
import mods.betterfoliage.config.match.MatchResult
|
||||
import mods.betterfoliage.config.match.MatchRuleList
|
||||
import mods.betterfoliage.config.match.Node
|
||||
import mods.betterfoliage.util.HasLogger
|
||||
import net.minecraft.client.renderer.model.BlockModel
|
||||
import net.minecraft.client.renderer.model.VariantList
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import org.apache.logging.log4j.Level
|
||||
|
||||
abstract class ParametrizedModelDiscovery : HasLogger() {
|
||||
abstract fun processModel(ctx: ModelDiscoveryContext, params: Map<String, String>)
|
||||
|
||||
fun Map<String, String>.texture(key: String): ResourceLocation? {
|
||||
val result = get(key)?.let { ResourceLocation(it) }
|
||||
if (result == null) detailLogger.log(Level.WARN, "Cannot find texture parameter \"$key\"")
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
class RuleProcessingContext(
|
||||
val discovery: ModelDiscoveryContext
|
||||
) {
|
||||
val params = mutableMapOf("type" to "none")
|
||||
}
|
||||
|
||||
class RuleBasedDiscovery : AbstractModelDiscovery() {
|
||||
val discoverers = mutableMapOf<String, ParametrizedModelDiscovery>()
|
||||
|
||||
override fun processModel(ctx: ModelDiscoveryContext) = when(ctx.getUnbaked()) {
|
||||
is VariantList -> processContainerModel(ctx)
|
||||
is BlockModel -> processBlockModel(ctx)
|
||||
else -> Unit
|
||||
}
|
||||
|
||||
fun processBlockModel(ctx: ModelDiscoveryContext) {
|
||||
val ruleCtx = RuleProcessingContext(ctx)
|
||||
val rulesToCheck = BetterFoliage.blockConfig.rules.toMutableList()
|
||||
val ruleResults = mutableListOf<MatchResult>()
|
||||
var previousSize = 0
|
||||
|
||||
// stop processing if nothing changes anymore
|
||||
while (rulesToCheck.size != previousSize) {
|
||||
previousSize = rulesToCheck.size
|
||||
val iterator = rulesToCheck.listIterator()
|
||||
while (iterator.hasNext()) iterator.next().let { rule ->
|
||||
// process single rule
|
||||
MatchRuleList.visitRoot(ruleCtx, rule).let { result ->
|
||||
ruleResults.add(result)
|
||||
// remove rule from active list if:
|
||||
// - rule succeeded (all directives returned success)
|
||||
// - rule is invariant (result will always be the same)
|
||||
if (result.isSuccess || result.isInvariant) iterator.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// log result of rule processing
|
||||
if (ruleResults.any { it.isSuccess }) {
|
||||
detailLogger.log(Level.INFO, "================================")
|
||||
detailLogger.log(Level.INFO, "block state: ${ctx.blockState}")
|
||||
detailLogger.log(Level.INFO, "block class: ${ctx.blockState.block::class.java.name}")
|
||||
detailLogger.log(Level.INFO, "model : ${ctx.modelLocation}")
|
||||
detailLogger.log(Level.INFO, "--------------------------------")
|
||||
ruleResults.forEach { result ->
|
||||
if (result !is MatchResult.RootList || result.results.shouldLog())
|
||||
result.log { source, message -> detailLogger.log(Level.INFO, "[$source] $message") }
|
||||
}
|
||||
}
|
||||
|
||||
discoverers[ruleCtx.params["type"]]?.processModel(ctx, ruleCtx.params)
|
||||
}
|
||||
|
||||
fun List<MatchResult>.shouldLog() = all { it.isSuccess } || fold(false) { seenInvariantSuccess, result ->
|
||||
seenInvariantSuccess || (result.isSuccess && result.isInvariant)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user