diff --git a/gradle.properties b/gradle.properties index deeda5d4..6850f874 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,6 +7,6 @@ org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled javaVersion=25 mcVersion=26.1.2 group=dev.slne.surf.api -version=3.16.0 +version=3.17.0 relocationPrefix=dev.slne.surf.api.libs snapshot=false diff --git a/surf-api-core/surf-api-core/api/surf-api-core.api b/surf-api-core/surf-api-core/api/surf-api-core.api index ded82280..6b54ca04 100644 --- a/surf-api-core/surf-api-core/api/surf-api-core.api +++ b/surf-api-core/surf-api-core/api/surf-api-core.api @@ -578,6 +578,22 @@ public final class dev/slne/surf/api/core/config/type/DurationOrDisabled { public final class dev/slne/surf/api/core/config/type/DurationOrDisabled$Companion { } +public final class dev/slne/surf/api/core/config/type/StringOrDefault { + public static final field Companion Ldev/slne/surf/api/core/config/type/StringOrDefault$Companion; + public synthetic fun (Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/lang/String; + public fun equals (Ljava/lang/Object;)Z + public final fun getValue ()Ljava/lang/String; + public fun hashCode ()I + public final fun or (Ljava/lang/String;)Ljava/lang/String; + public fun toString ()Ljava/lang/String; +} + +public final class dev/slne/surf/api/core/config/type/StringOrDefault$Companion { + public final fun getUSE_DEFAULT ()Ldev/slne/surf/api/core/config/type/StringOrDefault; + public final fun of (Ljava/lang/String;)Ldev/slne/surf/api/core/config/type/StringOrDefault; +} + public abstract interface annotation class dev/slne/surf/api/core/config/type/number/BelowZeroToEmpty : java/lang/annotation/Annotation { } diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/CollectionConstraintUtils.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/CollectionConstraintUtils.kt deleted file mode 100644 index b1f571f8..00000000 --- a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/CollectionConstraintUtils.kt +++ /dev/null @@ -1,10 +0,0 @@ -package dev.slne.surf.api.core.config.constraints - -internal fun Any?.configSizeOrNull(): Int? = when (this) { - null -> null - is Collection<*> -> size - is Map<*, *> -> size - is Array<*> -> size - is CharSequence -> length - else -> null -} \ No newline at end of file diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/Contains.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/Contains.kt index 46b77065..dfc758e3 100644 --- a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/Contains.kt +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/Contains.kt @@ -1,5 +1,6 @@ package dev.slne.surf.api.core.config.constraints +import dev.slne.surf.api.core.config.type.StringOrDefault import org.spongepowered.configurate.objectmapping.meta.Constraint import org.spongepowered.configurate.serialize.SerializationException import java.lang.reflect.Type @@ -23,5 +24,14 @@ annotation class Contains(val value: String) { } } } + + internal object FactoryStringOrDefault : Constraint.Factory { + override fun make(data: Contains, type: Type): Constraint = { valueOrDefault -> + val value = valueOrDefault?.value + if (value != null && !value.contains(data.value)) { + throw SerializationException("String must contain '${data.value}'") + } + } + } } } \ No newline at end of file diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/EndsWith.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/EndsWith.kt index c013118f..e6efae9e 100644 --- a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/EndsWith.kt +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/EndsWith.kt @@ -1,5 +1,6 @@ package dev.slne.surf.api.core.config.constraints +import dev.slne.surf.api.core.config.type.StringOrDefault import org.spongepowered.configurate.objectmapping.meta.Constraint import org.spongepowered.configurate.serialize.SerializationException import java.lang.reflect.Type @@ -23,5 +24,14 @@ annotation class EndsWith(val suffix: String) { } } } + + internal object FactoryStringOrDefault : Constraint.Factory { + override fun make(data: EndsWith, type: Type): Constraint = { stringOrDefault -> + val value = stringOrDefault?.value + if (value != null && !value.endsWith(data.suffix)) { + throw SerializationException("String must end with '${data.suffix}'") + } + } + } } } \ No newline at end of file diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/ExistingFile.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/ExistingFile.kt index 04759854..4e91ea7c 100644 --- a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/ExistingFile.kt +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/ExistingFile.kt @@ -1,5 +1,6 @@ package dev.slne.surf.api.core.config.constraints +import dev.slne.surf.api.core.config.type.StringOrDefault import org.spongepowered.configurate.objectmapping.meta.Constraint import org.spongepowered.configurate.serialize.SerializationException import java.io.File @@ -41,12 +42,13 @@ annotation class ExistingFile { } } -internal fun Any?.asPathOrNull(): Path? { +internal tailrec fun Any?.asPathOrNull(): Path? { return when (this) { null -> null is Path -> this is File -> toPath() is String -> runCatching { Path.of(this) }.getOrNull() + is StringOrDefault -> value?.asPathOrNull() else -> null } } \ No newline at end of file diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/MaxDuration.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/MaxDuration.kt index 1bd48122..2f46c274 100644 --- a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/MaxDuration.kt +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/MaxDuration.kt @@ -1,6 +1,7 @@ package dev.slne.surf.api.core.config.constraints import dev.slne.surf.api.core.config.type.ConfigDuration +import dev.slne.surf.api.core.config.type.DurationOrDisabled import org.spongepowered.configurate.objectmapping.meta.Constraint import org.spongepowered.configurate.serialize.SerializationException import java.lang.reflect.Type @@ -25,5 +26,15 @@ annotation class MaxDuration(val seconds: Long) { } } } + + internal object FactoryDurationOrDisabled : Constraint.Factory { + override fun make(data: MaxDuration, type: Type): Constraint = { durationOrDisabled -> + val value = durationOrDisabled?.value + + if (value != null && value.inWholeSeconds > data.seconds) { + throw SerializationException("Duration is too long: ${value}, expected <= ${data.seconds}s") + } + } + } } } \ No newline at end of file diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/MaxLength.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/MaxLength.kt index 38f979de..471cce5f 100644 --- a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/MaxLength.kt +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/MaxLength.kt @@ -1,5 +1,6 @@ package dev.slne.surf.api.core.config.constraints +import dev.slne.surf.api.core.config.type.StringOrDefault import org.spongepowered.configurate.objectmapping.meta.Constraint import org.spongepowered.configurate.serialize.SerializationException import java.lang.reflect.Type @@ -24,5 +25,14 @@ annotation class MaxLength(val max: Int) { } } } + + internal object FactoryStringOrDefault : Constraint.Factory { + override fun make(data: MaxLength, type: Type): Constraint = { stringOrDefault -> + val value = stringOrDefault?.value + if (value != null && value.length > data.max) { + throw SerializationException("String is too long: ${value.length}, expected <= ${data.max}") + } + } + } } } \ No newline at end of file diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/MaxNumber.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/MaxNumber.kt index cbe07763..85b64d0c 100644 --- a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/MaxNumber.kt +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/MaxNumber.kt @@ -1,5 +1,7 @@ package dev.slne.surf.api.core.config.constraints +import dev.slne.surf.api.core.config.type.number.DoubleOr +import dev.slne.surf.api.core.config.type.number.IntOr import org.spongepowered.configurate.objectmapping.meta.Constraint import org.spongepowered.configurate.serialize.SerializationException import java.lang.reflect.Type @@ -35,5 +37,25 @@ annotation class MaxNumber(val max: Double) { } } } + + internal object FactoryIntOr : Constraint.Factory { + override fun make(data: MaxNumber, type: Type): Constraint = { intOr -> + val number = intOr?.value + + if (number != null && number > data.max) { + throw SerializationException(type, "Number is too big: $number, expected <= ${data.max}") + } + } + } + + internal object FactoryDoubleOr : Constraint.Factory { + override fun make(data: MaxNumber, type: Type): Constraint = { doubleOr -> + val number = doubleOr?.value + + if (number != null && number > data.max) { + throw SerializationException(type, "Number is too big: $number, expected <= ${data.max}") + } + } + } } } diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/MinDuration.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/MinDuration.kt index 85795969..2684b882 100644 --- a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/MinDuration.kt +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/MinDuration.kt @@ -1,6 +1,7 @@ package dev.slne.surf.api.core.config.constraints import dev.slne.surf.api.core.config.type.ConfigDuration +import dev.slne.surf.api.core.config.type.DurationOrDisabled import org.spongepowered.configurate.objectmapping.meta.Constraint import org.spongepowered.configurate.serialize.SerializationException import java.lang.reflect.Type @@ -25,5 +26,15 @@ annotation class MinDuration(val seconds: Long) { } } } + + internal object FactoryDurationOrDisabled : Constraint.Factory { + override fun make(data: MinDuration, type: Type): Constraint = { durationOrDisabled -> + val value = durationOrDisabled?.value + + if (value != null && value.inWholeSeconds < data.seconds) { + throw SerializationException("Duration is too short: ${value}, expected >= ${data.seconds}s") + } + } + } } } \ No newline at end of file diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/MinLength.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/MinLength.kt index 81f5985b..cd12b69e 100644 --- a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/MinLength.kt +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/MinLength.kt @@ -1,5 +1,6 @@ package dev.slne.surf.api.core.config.constraints +import dev.slne.surf.api.core.config.type.StringOrDefault import org.spongepowered.configurate.objectmapping.meta.Constraint import org.spongepowered.configurate.serialize.SerializationException import java.lang.reflect.Type @@ -23,5 +24,15 @@ annotation class MinLength(val min: Int) { } } } + + internal object FactoryStringOrDefault : Constraint.Factory { + override fun make(data: MinLength, type: Type): Constraint = { stringOrDefault -> + val value = stringOrDefault?.value + + if (value != null && value.length < data.min) { + throw SerializationException("String is too short: ${value.length}, expected >= ${data.min}") + } + } + } } } diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/MinNumber.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/MinNumber.kt index 1d6edca4..a2c87d99 100644 --- a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/MinNumber.kt +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/MinNumber.kt @@ -1,5 +1,7 @@ package dev.slne.surf.api.core.config.constraints +import dev.slne.surf.api.core.config.type.number.DoubleOr +import dev.slne.surf.api.core.config.type.number.IntOr import org.spongepowered.configurate.objectmapping.meta.Constraint import org.spongepowered.configurate.serialize.SerializationException import java.lang.reflect.Type @@ -35,6 +37,26 @@ annotation class MinNumber(val min: Double) { } } } + + internal object FactoryIntOr : Constraint.Factory { + override fun make(data: MinNumber, type: Type): Constraint = { intOr -> + val number = intOr?.value + + if (number != null && number < data.min) { + throw SerializationException(type, "Number is too small: $number, expected >= ${data.min}") + } + } + } + + internal object FactoryDoubleOr : Constraint.Factory { + override fun make(data: MinNumber, type: Type): Constraint = { doubleOr -> + val number = doubleOr?.value + + if (number != null && number < data.min) { + throw SerializationException(type, "Number is too small: $number, expected >= ${data.min}") + } + } + } } } diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/NegativeNumber.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/NegativeNumber.kt index f28f51da..8c48e515 100644 --- a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/NegativeNumber.kt +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/NegativeNumber.kt @@ -1,5 +1,7 @@ package dev.slne.surf.api.core.config.constraints +import dev.slne.surf.api.core.config.type.number.DoubleOr +import dev.slne.surf.api.core.config.type.number.IntOr import org.spongepowered.configurate.objectmapping.meta.Constraint import org.spongepowered.configurate.serialize.SerializationException import java.lang.reflect.Type @@ -21,5 +23,25 @@ annotation class NegativeNumber { } } } + + internal object FactoryIntOr : Constraint.Factory { + override fun make(data: NegativeNumber, type: Type): Constraint = { intOr -> + val value = intOr?.value + + if (value != null && value >= 0) { + throw SerializationException("Number must be negative: $value, expected < 0") + } + } + } + + internal object FactoryDoubleOr : Constraint.Factory { + override fun make(data: NegativeNumber, type: Type): Constraint = { doubleOr -> + val value = doubleOr?.value + + if (value != null && value >= 0.0) { + throw SerializationException("Number must be negative: $value, expected < 0") + } + } + } } } \ No newline at end of file diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/PositiveNumber.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/PositiveNumber.kt index cad0081e..9a7000ca 100644 --- a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/PositiveNumber.kt +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/PositiveNumber.kt @@ -1,5 +1,7 @@ package dev.slne.surf.api.core.config.constraints +import dev.slne.surf.api.core.config.type.number.DoubleOr +import dev.slne.surf.api.core.config.type.number.IntOr import org.spongepowered.configurate.objectmapping.meta.Constraint import org.spongepowered.configurate.serialize.SerializationException import java.lang.reflect.Type @@ -29,7 +31,27 @@ annotation class PositiveNumber { */ internal object Factory : Constraint.Factory { override fun make(data: PositiveNumber, type: Type): Constraint = { number -> - if (number != null && number.toDouble() <= 0) { + if (number != null && number.toDouble() <= 0.0) { + throw SerializationException("Number is not positive: $number, expected > 0") + } + } + } + + internal object FactoryIntOr : Constraint.Factory { + override fun make(data: PositiveNumber, type: Type): Constraint = { intOr -> + val number = intOr?.value + + if (number != null && number <= 0) { + throw SerializationException("Number is not positive: $number, expected > 0") + } + } + } + + internal object FactoryDoubleOr : Constraint.Factory { + override fun make(data: PositiveNumber, type: Type): Constraint = { doubleOr -> + val number = doubleOr?.value + + if (number != null && number <= 0.0) { throw SerializationException("Number is not positive: $number, expected > 0") } } diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/Range.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/Range.kt index 26ca037c..868b8011 100644 --- a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/Range.kt +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/Range.kt @@ -1,5 +1,7 @@ package dev.slne.surf.api.core.config.constraints +import dev.slne.surf.api.core.config.type.number.DoubleOr +import dev.slne.surf.api.core.config.type.number.IntOr import org.spongepowered.configurate.objectmapping.meta.Constraint import org.spongepowered.configurate.serialize.SerializationException import java.lang.reflect.Type @@ -27,5 +29,30 @@ annotation class Range(val min: Double, val max: Double) { } } } + + internal object FactoryIntOr : Constraint.Factory { + override fun make(data: Range, type: Type): Constraint = { intOr -> + val value = intOr?.value + + if (value != null) { + val double = value.toDouble() + if (double < data.min || double > data.max) { + throw SerializationException("Number is out of range: $value, expected ${data.min}..${data.max}") + } + } + } + } + + internal object FactoryDoubleOr : Constraint.Factory { + override fun make(data: Range, type: Type): Constraint = { doubleOr -> + val value = doubleOr?.value + + if (value != null) { + if (value < data.min || value > data.max) { + throw SerializationException("Number is out of range: $value, expected ${data.min}..${data.max}") + } + } + } + } } } \ No newline at end of file diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/StartsWith.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/StartsWith.kt index 255a28ab..642ac983 100644 --- a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/StartsWith.kt +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/constraints/StartsWith.kt @@ -1,5 +1,6 @@ package dev.slne.surf.api.core.config.constraints +import dev.slne.surf.api.core.config.type.StringOrDefault import org.spongepowered.configurate.objectmapping.meta.Constraint import org.spongepowered.configurate.serialize.SerializationException import java.lang.reflect.Type @@ -23,5 +24,15 @@ annotation class StartsWith(val prefix: String) { } } } + + internal object FactoryStringOrDefault : Constraint.Factory { + override fun make(data: StartsWith, type: Type): Constraint = { stringOrDefault -> + val value = stringOrDefault?.value + + if (value != null && !value.startsWith(data.prefix)) { + throw SerializationException("String must start with '${data.prefix}'") + } + } + } } } diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/serializer/SpongeConfigSerializers.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/serializer/SpongeConfigSerializers.kt index e36053d5..8dc2719a 100644 --- a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/serializer/SpongeConfigSerializers.kt +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/serializer/SpongeConfigSerializers.kt @@ -6,6 +6,7 @@ import dev.slne.surf.api.core.config.serializer.collection.map.MapSerializer import dev.slne.surf.api.core.config.type.BooleanOrDefault import dev.slne.surf.api.core.config.type.ConfigDuration import dev.slne.surf.api.core.config.type.DurationOrDisabled +import dev.slne.surf.api.core.config.type.StringOrDefault import dev.slne.surf.api.core.config.type.number.DoubleOr import dev.slne.surf.api.core.config.type.number.IntOr import dev.slne.surf.api.core.minimessage.SurfMiniMessageHolder @@ -87,6 +88,7 @@ abstract class SpongeConfigSerializers { builder.register(KeySerializer) builder.register(ConfigDuration.Serializer) builder.register(BooleanOrDefault.Serializer) + builder.register(StringOrDefault.Serializer) builder.register(DurationOrDisabled.Serializer) builder.register(IntOr.Default.Serializer) builder.register(IntOr.Disabled.Serializer) @@ -152,23 +154,40 @@ abstract class SpongeConfigSerializers { ObjectMapper.factoryBuilder() .addDiscoverer(dataClassFieldDiscoverer()) .addConstraint(PositiveNumber.Companion.Factory) + .addConstraint(PositiveNumber.Companion.FactoryIntOr) + .addConstraint(PositiveNumber.Companion.FactoryDoubleOr) .addConstraint(NegativeNumber.Companion.Factory) + .addConstraint(NegativeNumber.Companion.FactoryIntOr) + .addConstraint(NegativeNumber.Companion.FactoryDoubleOr) .addConstraint(MinNumber.Companion.Factory) + .addConstraint(MinNumber.Companion.FactoryIntOr) + .addConstraint(MinNumber.Companion.FactoryDoubleOr) .addConstraint(MaxNumber.Companion.Factory) + .addConstraint(MaxNumber.Companion.FactoryIntOr) + .addConstraint(MaxNumber.Companion.FactoryDoubleOr) .addConstraint(NotBlank.Companion.Factory) .addConstraint(Trimmed.Companion.Factory) .addConstraint(MaxLength.Companion.Factory) + .addConstraint(MaxLength.Companion.FactoryStringOrDefault) .addConstraint(MinLength.Companion.Factory) + .addConstraint(MinLength.Companion.FactoryStringOrDefault) .addConstraint(StartsWith.Companion.Factory) + .addConstraint(StartsWith.Companion.FactoryStringOrDefault) .addConstraint(EndsWith.Companion.Factory) + .addConstraint(EndsWith.Companion.FactoryStringOrDefault) .addConstraint(Contains.Companion.Factory) + .addConstraint(Contains.Companion.FactoryStringOrDefault) .addConstraint(Range.Companion.Factory) + .addConstraint(Range.Companion.FactoryIntOr) + .addConstraint(Range.Companion.FactoryDoubleOr) .addConstraint(MinSize.Companion.Factory) .addConstraint(MaxSize.Companion.Factory) .addConstraint(NotEmpty.Companion.Factory) .addConstraint(NoDuplicates.Companion.Factory) .addConstraint(MinDuration.Companion.Factory) + .addConstraint(MinDuration.Companion.FactoryDurationOrDisabled) .addConstraint(MaxDuration.Companion.Factory) + .addConstraint(MaxDuration.Companion.FactoryDurationOrDisabled) .addConstraint(DisallowValues.Companion.Factory) .addConstraint(Namespace.Companion.Factory) .addConstraint(ExistingFile.Companion.Factory) diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/type/StringOrDefault.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/type/StringOrDefault.kt new file mode 100644 index 00000000..28d1cf4e --- /dev/null +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/config/type/StringOrDefault.kt @@ -0,0 +1,65 @@ +package dev.slne.surf.api.core.config.type + +import org.spongepowered.configurate.serialize.ScalarSerializer +import java.lang.reflect.AnnotatedType +import java.util.function.Predicate + +/** + * Represents a configuration value that can either hold a string or use a default fallback value. + * + * The class provides utility to handle values that might explicitly contain a string or defer + * to a caller-provided default value if none is specified. + * + * Features: + * - Allows explicit value encapsulation via the `of` method. + * - Supports the `USE_DEFAULT` singleton to represent fallback default behavior. + * - Provides an `or` infix function to resolve the value with a provided default. + * + * Serialization: + * - The `Serializer` handles the string representation of the value for use in configurations. + * - Serialized values include explicit strings or an internal `__default__` marker for default representation. + * + * Companion Object: + * - Contains helper methods and a constant (`USE_DEFAULT`) for working with default behavior. + */ +@ConsistentCopyVisibility +data class StringOrDefault private constructor(val value: String?) { + + infix fun or(default: String): String = value ?: default + + override fun toString(): String { + return value ?: DEFAULT_MARKER + } + + companion object { + private const val DEFAULT_MARKER = "__default__" + + @JvmField + val USE_DEFAULT = StringOrDefault(null) + + fun of(value: String) = StringOrDefault(value) + } + + internal object Serializer : ScalarSerializer.Annotated(StringOrDefault::class.java) { + override fun deserialize( + type: AnnotatedType?, + obj: Any? + ): StringOrDefault? { + val value = obj?.toString() + + return if (value == null || value == DEFAULT_MARKER) { + USE_DEFAULT + } else { + StringOrDefault(value) + } + } + + override fun serialize( + type: AnnotatedType?, + item: StringOrDefault?, + typeSupported: Predicate?>? + ): Any { + return item?.value ?: DEFAULT_MARKER + } + } +} \ No newline at end of file