feat(default-exts): Introduce presets extensions, use in validation fallback
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
package de.siphalor.tweed5.defaultextensions.presets.api;
|
||||
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.extension.TweedExtension;
|
||||
import de.siphalor.tweed5.defaultextensions.presets.impl.PresetsExtensionImpl;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
public interface PresetsExtension extends TweedExtension {
|
||||
Class<? extends PresetsExtension> DEFAULT = PresetsExtensionImpl.class;
|
||||
String EXTENSION_ID = "presets";
|
||||
|
||||
String DEFAULT_PRESET_NAME = "default";
|
||||
|
||||
@Override
|
||||
default String getId() {
|
||||
return EXTENSION_ID;
|
||||
}
|
||||
|
||||
static <C extends ConfigEntry<T>, T> Consumer<C> presetValue(String name, T value) {
|
||||
return entry -> {
|
||||
PresetsExtension extension = entry.container().extension(PresetsExtension.class)
|
||||
.orElseThrow(() -> new IllegalStateException("No presets extension registered"));
|
||||
extension.presetValue(entry, name, value);
|
||||
};
|
||||
}
|
||||
|
||||
static <C extends ConfigEntry<T>, T> Function<C, @Nullable T> presetValue(String name) {
|
||||
return entry -> {
|
||||
PresetsExtension extension = entry.container().extension(PresetsExtension.class)
|
||||
.orElseThrow(() -> new IllegalStateException("No presets extension registered"));
|
||||
return extension.presetValue(entry, name);
|
||||
};
|
||||
}
|
||||
|
||||
<T extends @Nullable Object> void presetValue(ConfigEntry<T> entry, String name, T value);
|
||||
|
||||
<T extends @Nullable Object> @Nullable T presetValue(ConfigEntry<T> entry, String name);
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
@NullMarked
|
||||
package de.siphalor.tweed5.defaultextensions.presets.api;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
@@ -0,0 +1,54 @@
|
||||
package de.siphalor.tweed5.defaultextensions.presets.impl;
|
||||
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntryValueVisitor;
|
||||
import de.siphalor.tweed5.core.api.extension.TweedExtensionSetupContext;
|
||||
import de.siphalor.tweed5.defaultextensions.presets.api.PresetsExtension;
|
||||
import de.siphalor.tweed5.patchwork.api.Patchwork;
|
||||
import de.siphalor.tweed5.patchwork.api.PatchworkPartAccess;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class PresetsExtensionImpl implements PresetsExtension {
|
||||
private final PatchworkPartAccess<Map<String, Object>> presetsDataAccess;
|
||||
|
||||
public PresetsExtensionImpl(TweedExtensionSetupContext setupContext) {
|
||||
//noinspection unchecked
|
||||
presetsDataAccess = setupContext.registerEntryExtensionData((Class<Map<String, Object>>)(Class<?>) Map.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends @Nullable Object> void presetValue(ConfigEntry<T> entry, String name, T value) {
|
||||
if (value != null && !entry.valueClass().isAssignableFrom(value.getClass())) {
|
||||
throw new IllegalArgumentException(
|
||||
"The preset value is not of the expected type " + entry.valueClass().getName()
|
||||
);
|
||||
}
|
||||
entry.visitInOrder(new ConfigEntryValueVisitor() {
|
||||
@Override
|
||||
public <U> void visitEntry(ConfigEntry<U> entry, U value) {
|
||||
getOrCreatePresetsData(entry.extensionsData()).put(name, value);
|
||||
}
|
||||
}, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> @Nullable T presetValue(ConfigEntry<T> entry, String name) {
|
||||
//noinspection unchecked
|
||||
return (T) getPresetsData(entry.extensionsData()).get(name);
|
||||
}
|
||||
|
||||
private Map<String, Object> getOrCreatePresetsData(Patchwork extensionsData) {
|
||||
Map<String, Object> data = extensionsData.get(presetsDataAccess);
|
||||
if (data == null) {
|
||||
extensionsData.set(presetsDataAccess, data = new HashMap<>());
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
private Map<String, Object> getPresetsData(Patchwork extensionsData) {
|
||||
Map<String, Object> data = extensionsData.get(presetsDataAccess);
|
||||
return data != null ? data : Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
@ApiStatus.Internal
|
||||
@NullMarked
|
||||
package de.siphalor.tweed5.defaultextensions.presets.impl;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
@@ -1,11 +1,8 @@
|
||||
package de.siphalor.tweed5.defaultextensions.validationfallback.api;
|
||||
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.extension.TweedExtension;
|
||||
import de.siphalor.tweed5.defaultextensions.validationfallback.impl.ValidationFallbackExtensionImpl;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public interface ValidationFallbackExtension extends TweedExtension {
|
||||
Class<? extends ValidationFallbackExtension> DEFAULT = ValidationFallbackExtensionImpl.class;
|
||||
String EXTENSION_ID = "validation-fallback";
|
||||
@@ -15,13 +12,5 @@ public interface ValidationFallbackExtension extends TweedExtension {
|
||||
return EXTENSION_ID;
|
||||
}
|
||||
|
||||
static <C extends ConfigEntry<T>, T> Consumer<C> validationFallbackValue(T value) {
|
||||
return entry -> {
|
||||
ValidationFallbackExtension extension = entry.container().extension(ValidationFallbackExtension.class)
|
||||
.orElseThrow(() -> new IllegalStateException("ValidationFallbackExtension is not registered"));
|
||||
extension.setFallbackValue(entry, value);
|
||||
};
|
||||
}
|
||||
|
||||
<T> void setFallbackValue(ConfigEntry<T> entry, T value);
|
||||
void fallbackToPreset(String presetName);
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
package de.siphalor.tweed5.defaultextensions.validationfallback.impl;
|
||||
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.extension.TweedExtensionSetupContext;
|
||||
import de.siphalor.tweed5.core.api.middleware.Middleware;
|
||||
import de.siphalor.tweed5.defaultextensions.presets.api.PresetsExtension;
|
||||
import de.siphalor.tweed5.defaultextensions.validation.api.ConfigEntryValidator;
|
||||
import de.siphalor.tweed5.defaultextensions.validation.api.ValidationProvidingExtension;
|
||||
import de.siphalor.tweed5.defaultextensions.validation.api.result.ValidationIssue;
|
||||
import de.siphalor.tweed5.defaultextensions.validation.api.result.ValidationIssueLevel;
|
||||
import de.siphalor.tweed5.defaultextensions.validation.api.result.ValidationResult;
|
||||
import de.siphalor.tweed5.defaultextensions.validationfallback.api.ValidationFallbackExtension;
|
||||
import de.siphalor.tweed5.patchwork.api.PatchworkPartAccess;
|
||||
import lombok.Data;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -19,25 +19,27 @@ import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ValidationFallbackExtensionImpl implements ValidationFallbackExtension, ValidationProvidingExtension {
|
||||
private final ConfigContainer<?> configContainer;
|
||||
private @Nullable PresetsExtension presetsExtension;
|
||||
|
||||
private final PatchworkPartAccess<CustomEntryData> customEntryDataAccess;
|
||||
private String fallbackPresetName = PresetsExtension.DEFAULT_PRESET_NAME;
|
||||
|
||||
public ValidationFallbackExtensionImpl(TweedExtensionSetupContext context) {
|
||||
customEntryDataAccess = context.registerEntryExtensionData(CustomEntryData.class);
|
||||
public ValidationFallbackExtensionImpl(TweedExtensionSetupContext context, ConfigContainer<?> configContainer) {
|
||||
this.configContainer = configContainer;
|
||||
context.registerExtension(PresetsExtension.class);
|
||||
}
|
||||
|
||||
private PresetsExtension getOrResolvePresetsExtension() {
|
||||
if (presetsExtension == null) {
|
||||
presetsExtension = configContainer.extension(PresetsExtension.class)
|
||||
.orElseThrow(() -> new IllegalStateException("No presets extension registered"));
|
||||
}
|
||||
return presetsExtension;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void setFallbackValue(ConfigEntry<T> entry, T value) {
|
||||
getOrCreateCustomEntryData(entry).fallbackValue(value);
|
||||
}
|
||||
|
||||
private CustomEntryData getOrCreateCustomEntryData(ConfigEntry<?> entry) {
|
||||
CustomEntryData customEntryData = entry.extensionsData().get(customEntryDataAccess);
|
||||
if (customEntryData == null) {
|
||||
customEntryData = new CustomEntryData();
|
||||
entry.extensionsData().set(customEntryDataAccess, customEntryData);
|
||||
}
|
||||
return customEntryData;
|
||||
public void fallbackToPreset(String presetName) {
|
||||
this.fallbackPresetName = presetName;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -45,11 +47,6 @@ public class ValidationFallbackExtensionImpl implements ValidationFallbackExtens
|
||||
return new ValidationFallbackMiddleware();
|
||||
}
|
||||
|
||||
@Data
|
||||
private static class CustomEntryData {
|
||||
@Nullable Object fallbackValue;
|
||||
}
|
||||
|
||||
private class ValidationFallbackMiddleware implements Middleware<ConfigEntryValidator> {
|
||||
@Override
|
||||
public String id() {
|
||||
@@ -75,16 +72,12 @@ public class ValidationFallbackExtensionImpl implements ValidationFallbackExtens
|
||||
if (!result.hasError()) {
|
||||
return result;
|
||||
}
|
||||
CustomEntryData entryData = configEntry.extensionsData().get(customEntryDataAccess);
|
||||
if (entryData == null) {
|
||||
return result;
|
||||
}
|
||||
PresetsExtension presetsExtension = getOrResolvePresetsExtension();
|
||||
|
||||
Object fallbackValue = entryData.fallbackValue();
|
||||
T fallbackValue = presetsExtension.presetValue(configEntry, fallbackPresetName);
|
||||
if (fallbackValue != null) {
|
||||
if (configEntry.valueClass().isInstance(fallbackValue)) {
|
||||
//noinspection unchecked
|
||||
fallbackValue = configEntry.deepCopy((T) fallbackValue);
|
||||
fallbackValue = configEntry.deepCopy(fallbackValue);
|
||||
} else {
|
||||
ArrayList<ValidationIssue> issues = new ArrayList<>(result.issues());
|
||||
issues.add(new ValidationIssue(
|
||||
@@ -96,9 +89,8 @@ public class ValidationFallbackExtensionImpl implements ValidationFallbackExtens
|
||||
}
|
||||
}
|
||||
|
||||
//noinspection unchecked
|
||||
return ValidationResult.withIssues(
|
||||
(T) fallbackValue,
|
||||
fallbackValue,
|
||||
result.issues().stream()
|
||||
.map(issue -> new ValidationIssue(
|
||||
issue.message(),
|
||||
@@ -112,11 +104,8 @@ public class ValidationFallbackExtensionImpl implements ValidationFallbackExtens
|
||||
|
||||
@Override
|
||||
public <T> String description(ConfigEntry<T> configEntry) {
|
||||
CustomEntryData entryData = configEntry.extensionsData().get(customEntryDataAccess);
|
||||
if (entryData == null) {
|
||||
return inner.description(configEntry);
|
||||
}
|
||||
return inner.description(configEntry) + "\n\nDefault/Fallback value: " + entryData.fallbackValue();
|
||||
T fallbackValue = getOrResolvePresetsExtension().presetValue(configEntry, fallbackPresetName);
|
||||
return inner.description(configEntry) + "\n\nDefault/Fallback value: " + fallbackValue;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ import org.jspecify.annotations.Nullable;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import java.io.StringReader;
|
||||
@@ -29,11 +28,9 @@ import java.util.Collections;
|
||||
|
||||
import static de.siphalor.tweed5.data.extension.api.ReadWriteExtension.*;
|
||||
import static de.siphalor.tweed5.data.extension.api.readwrite.TweedEntryReaderWriters.*;
|
||||
import static de.siphalor.tweed5.defaultextensions.validation.api.ValidationExtension.validate;
|
||||
import static de.siphalor.tweed5.defaultextensions.presets.api.PresetsExtension.presetValue;
|
||||
import static de.siphalor.tweed5.defaultextensions.validation.api.ValidationExtension.validators;
|
||||
import static de.siphalor.tweed5.defaultextensions.validationfallback.api.ValidationFallbackExtension.validationFallbackValue;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class ValidationFallbackExtensionImplTest {
|
||||
private DefaultConfigContainer<@Nullable Integer> configContainer;
|
||||
@@ -106,7 +103,9 @@ class ValidationFallbackExtensionImplTest {
|
||||
}
|
||||
}
|
||||
))
|
||||
.apply(validationFallbackValue(3));
|
||||
.apply(presetValue("custom", 3));
|
||||
|
||||
configContainer.extension(ValidationFallbackExtension.class).orElseThrow().fallbackToPreset("custom");
|
||||
|
||||
configContainer.attachTree(intEntry);
|
||||
configContainer.initialize();
|
||||
|
||||
Reference in New Issue
Block a user