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;
|
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.core.api.extension.TweedExtension;
|
||||||
import de.siphalor.tweed5.defaultextensions.validationfallback.impl.ValidationFallbackExtensionImpl;
|
import de.siphalor.tweed5.defaultextensions.validationfallback.impl.ValidationFallbackExtensionImpl;
|
||||||
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
public interface ValidationFallbackExtension extends TweedExtension {
|
public interface ValidationFallbackExtension extends TweedExtension {
|
||||||
Class<? extends ValidationFallbackExtension> DEFAULT = ValidationFallbackExtensionImpl.class;
|
Class<? extends ValidationFallbackExtension> DEFAULT = ValidationFallbackExtensionImpl.class;
|
||||||
String EXTENSION_ID = "validation-fallback";
|
String EXTENSION_ID = "validation-fallback";
|
||||||
@@ -15,13 +12,5 @@ public interface ValidationFallbackExtension extends TweedExtension {
|
|||||||
return EXTENSION_ID;
|
return EXTENSION_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
static <C extends ConfigEntry<T>, T> Consumer<C> validationFallbackValue(T value) {
|
void fallbackToPreset(String presetName);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
package de.siphalor.tweed5.defaultextensions.validationfallback.impl;
|
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.entry.ConfigEntry;
|
||||||
import de.siphalor.tweed5.core.api.extension.TweedExtensionSetupContext;
|
import de.siphalor.tweed5.core.api.extension.TweedExtensionSetupContext;
|
||||||
import de.siphalor.tweed5.core.api.middleware.Middleware;
|
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.ConfigEntryValidator;
|
||||||
import de.siphalor.tweed5.defaultextensions.validation.api.ValidationProvidingExtension;
|
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.ValidationIssue;
|
||||||
import de.siphalor.tweed5.defaultextensions.validation.api.result.ValidationIssueLevel;
|
import de.siphalor.tweed5.defaultextensions.validation.api.result.ValidationIssueLevel;
|
||||||
import de.siphalor.tweed5.defaultextensions.validation.api.result.ValidationResult;
|
import de.siphalor.tweed5.defaultextensions.validation.api.result.ValidationResult;
|
||||||
import de.siphalor.tweed5.defaultextensions.validationfallback.api.ValidationFallbackExtension;
|
import de.siphalor.tweed5.defaultextensions.validationfallback.api.ValidationFallbackExtension;
|
||||||
import de.siphalor.tweed5.patchwork.api.PatchworkPartAccess;
|
|
||||||
import lombok.Data;
|
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -19,25 +19,27 @@ import java.util.Set;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class ValidationFallbackExtensionImpl implements ValidationFallbackExtension, ValidationProvidingExtension {
|
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) {
|
public ValidationFallbackExtensionImpl(TweedExtensionSetupContext context, ConfigContainer<?> configContainer) {
|
||||||
customEntryDataAccess = context.registerEntryExtensionData(CustomEntryData.class);
|
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
|
@Override
|
||||||
public <T> void setFallbackValue(ConfigEntry<T> entry, T value) {
|
public void fallbackToPreset(String presetName) {
|
||||||
getOrCreateCustomEntryData(entry).fallbackValue(value);
|
this.fallbackPresetName = presetName;
|
||||||
}
|
|
||||||
|
|
||||||
private CustomEntryData getOrCreateCustomEntryData(ConfigEntry<?> entry) {
|
|
||||||
CustomEntryData customEntryData = entry.extensionsData().get(customEntryDataAccess);
|
|
||||||
if (customEntryData == null) {
|
|
||||||
customEntryData = new CustomEntryData();
|
|
||||||
entry.extensionsData().set(customEntryDataAccess, customEntryData);
|
|
||||||
}
|
|
||||||
return customEntryData;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -45,11 +47,6 @@ public class ValidationFallbackExtensionImpl implements ValidationFallbackExtens
|
|||||||
return new ValidationFallbackMiddleware();
|
return new ValidationFallbackMiddleware();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
|
||||||
private static class CustomEntryData {
|
|
||||||
@Nullable Object fallbackValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ValidationFallbackMiddleware implements Middleware<ConfigEntryValidator> {
|
private class ValidationFallbackMiddleware implements Middleware<ConfigEntryValidator> {
|
||||||
@Override
|
@Override
|
||||||
public String id() {
|
public String id() {
|
||||||
@@ -75,16 +72,12 @@ public class ValidationFallbackExtensionImpl implements ValidationFallbackExtens
|
|||||||
if (!result.hasError()) {
|
if (!result.hasError()) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
CustomEntryData entryData = configEntry.extensionsData().get(customEntryDataAccess);
|
PresetsExtension presetsExtension = getOrResolvePresetsExtension();
|
||||||
if (entryData == null) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object fallbackValue = entryData.fallbackValue();
|
T fallbackValue = presetsExtension.presetValue(configEntry, fallbackPresetName);
|
||||||
if (fallbackValue != null) {
|
if (fallbackValue != null) {
|
||||||
if (configEntry.valueClass().isInstance(fallbackValue)) {
|
if (configEntry.valueClass().isInstance(fallbackValue)) {
|
||||||
//noinspection unchecked
|
fallbackValue = configEntry.deepCopy(fallbackValue);
|
||||||
fallbackValue = configEntry.deepCopy((T) fallbackValue);
|
|
||||||
} else {
|
} else {
|
||||||
ArrayList<ValidationIssue> issues = new ArrayList<>(result.issues());
|
ArrayList<ValidationIssue> issues = new ArrayList<>(result.issues());
|
||||||
issues.add(new ValidationIssue(
|
issues.add(new ValidationIssue(
|
||||||
@@ -96,9 +89,8 @@ public class ValidationFallbackExtensionImpl implements ValidationFallbackExtens
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//noinspection unchecked
|
|
||||||
return ValidationResult.withIssues(
|
return ValidationResult.withIssues(
|
||||||
(T) fallbackValue,
|
fallbackValue,
|
||||||
result.issues().stream()
|
result.issues().stream()
|
||||||
.map(issue -> new ValidationIssue(
|
.map(issue -> new ValidationIssue(
|
||||||
issue.message(),
|
issue.message(),
|
||||||
@@ -112,11 +104,8 @@ public class ValidationFallbackExtensionImpl implements ValidationFallbackExtens
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> String description(ConfigEntry<T> configEntry) {
|
public <T> String description(ConfigEntry<T> configEntry) {
|
||||||
CustomEntryData entryData = configEntry.extensionsData().get(customEntryDataAccess);
|
T fallbackValue = getOrResolvePresetsExtension().presetValue(configEntry, fallbackPresetName);
|
||||||
if (entryData == null) {
|
return inner.description(configEntry) + "\n\nDefault/Fallback value: " + fallbackValue;
|
||||||
return inner.description(configEntry);
|
|
||||||
}
|
|
||||||
return inner.description(configEntry) + "\n\nDefault/Fallback value: " + entryData.fallbackValue();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import org.jspecify.annotations.Nullable;
|
|||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.CsvSource;
|
|
||||||
import org.junit.jupiter.params.provider.ValueSource;
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
|
|
||||||
import java.io.StringReader;
|
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.ReadWriteExtension.*;
|
||||||
import static de.siphalor.tweed5.data.extension.api.readwrite.TweedEntryReaderWriters.*;
|
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.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.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
|
|
||||||
class ValidationFallbackExtensionImplTest {
|
class ValidationFallbackExtensionImplTest {
|
||||||
private DefaultConfigContainer<@Nullable Integer> configContainer;
|
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.attachTree(intEntry);
|
||||||
configContainer.initialize();
|
configContainer.initialize();
|
||||||
|
|||||||
Reference in New Issue
Block a user