feat(config): Completely migrate to using JsonReader instead of JsonElement
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/jfmod Pipeline failed Details

This commit is contained in:
Johannes Frohnmeyer 2023-07-18 20:11:23 +02:00
parent 0f3933a946
commit d2c2af2049
Signed by: Johannes
GPG Key ID: E76429612C2929F4
9 changed files with 88 additions and 63 deletions

View File

@ -1,7 +1,8 @@
package io.gitlab.jfronny.libjf.config.api.v1;
import io.gitlab.jfronny.gson.JsonElement;
import io.gitlab.jfronny.gson.stream.JsonWriter;
import io.gitlab.jfronny.gson.JsonParseException;
import io.gitlab.jfronny.gson.stream.*;
import io.gitlab.jfronny.libjf.config.api.v1.type.Type;
import io.gitlab.jfronny.libjf.config.impl.dsl.DslEntryInfo;
import org.jetbrains.annotations.ApiStatus;
@ -61,7 +62,20 @@ public interface EntryInfo<T> {
* Set this entry's value to that of the element
* @param element The element to read from
*/
void loadFromJson(JsonElement element) throws IllegalAccessException;
@Deprecated
default void loadFromJson(JsonElement element) throws IllegalAccessException {
try {
loadFromJson(new JsonTreeReader(element));
} catch (IOException e) {
throw new JsonParseException("Could not read Json", e);
}
}
/**
* Set this entry's value to that of the element
* @param reader The reader to read from
*/
void loadFromJson(JsonReader reader) throws IOException, IllegalAccessException;
/**
* Write the currently cached value to the writer

View File

@ -1,6 +1,7 @@
package io.gitlab.jfronny.libjf.config.api.v1.dsl;
import io.gitlab.jfronny.gson.JsonElement;
import io.gitlab.jfronny.gson.stream.JsonReader;
import io.gitlab.jfronny.libjf.config.api.v1.*;
import io.gitlab.jfronny.libjf.config.api.v1.type.Type;
import org.jetbrains.annotations.ApiStatus;
@ -38,7 +39,7 @@ public interface CategoryBuilder<Builder extends CategoryBuilder<Builder>> {
<T> Builder value(String id, T def, double min, double max, Type type, int width, Supplier<T> get, Consumer<T> set);
<T> Builder value(EntryInfo<T> entry);
@ApiStatus.Experimental Builder addMigration(String element, Consumer<JsonElement> apply);
@ApiStatus.Experimental Builder addMigration(String element, Migration migration);
String getId();

View File

@ -0,0 +1,10 @@
package io.gitlab.jfronny.libjf.config.api.v1.dsl;
import io.gitlab.jfronny.gson.stream.JsonReader;
import java.io.IOException;
@FunctionalInterface
public interface Migration {
void apply(JsonReader reader) throws IOException;
}

View File

@ -1,9 +1,11 @@
package io.gitlab.jfronny.libjf.config.impl.dsl;
import io.gitlab.jfronny.gson.JsonElement;
import io.gitlab.jfronny.commons.throwable.Coerce;
import io.gitlab.jfronny.gson.stream.JsonReader;
import io.gitlab.jfronny.libjf.LibJf;
import io.gitlab.jfronny.libjf.config.api.v1.*;
import io.gitlab.jfronny.libjf.config.api.v1.dsl.CategoryBuilder;
import io.gitlab.jfronny.libjf.config.api.v1.dsl.Migration;
import io.gitlab.jfronny.libjf.config.api.v1.type.Type;
import java.util.*;
@ -19,7 +21,7 @@ public class CategoryBuilderImpl<Builder extends CategoryBuilderImpl<Builder>> i
public final Map<String, Consumer<ConfigCategory>> presets = new LinkedHashMap<>();
public final List<Supplier<List<ConfigInstance>>> referencedConfigs = new LinkedList<>();
public final List<Consumer<ConfigCategory>> verifiers = new LinkedList<>();
public final Map<String, Consumer<JsonElement>> migrations = new LinkedHashMap<>();
public final Map<String, Consumer<JsonReader>> migrations = new LinkedHashMap<>();
private boolean built = false;
public CategoryBuilderImpl(String id, String categoryPath) {
@ -169,10 +171,10 @@ public class CategoryBuilderImpl<Builder extends CategoryBuilderImpl<Builder>> i
}
@Override
public Builder addMigration(String element, Consumer<JsonElement> apply) {
public Builder addMigration(String element, Migration migration) {
checkBuilt();
if (migrations.containsKey(element)) LibJf.LOGGER.warn("Duplicate migration registered for " + categoryPath + id + ": " + element + ", overriding");
migrations.put(element, apply);
migrations.put(element, Coerce.consumer(migration::apply).addHandler(e -> LibJf.LOGGER.error("Could not apply migration for " + element, e)));
return asBuilder();
}

View File

@ -37,28 +37,30 @@ public class DefaultConfigIO {
private static void runActions(String id, Map<String, Action> actions, JsonReader reader) {
try {
if (reader.peek() == JsonToken.BEGIN_OBJECT) {
Set<String> appeared = new HashSet<>();
reader.beginObject();
while (reader.peek() != JsonToken.END_OBJECT) {
String name = reader.nextName();
if (!actions.containsKey(name)) {
LibJf.LOGGER.warn("Unrecognized key in config for " + id + ": " + name);
continue;
}
if (!appeared.add(name)) {
LibJf.LOGGER.warn("Duplicate key in config for " + id + ": " + name);
continue;
}
actions.get(name).task.accept(reader);
if (reader.peek() != JsonToken.BEGIN_OBJECT) {
LibJf.LOGGER.error("Invalid config: Not a JSON object for " + id);
return;
}
Set<String> appeared = new HashSet<>();
reader.beginObject();
while (reader.peek() != JsonToken.END_OBJECT) {
String name = reader.nextName();
if (!actions.containsKey(name)) {
LibJf.LOGGER.warn("Unrecognized key in config for " + id + ": " + name);
continue;
}
reader.endObject();
actions.forEach((name, action) -> {
if (action.required && !appeared.contains(name)) {
LibJf.LOGGER.error("Missing entry in config for " + id + ": " + name);
}
});
} LibJf.LOGGER.error("Invalid config: Not a JSON object for " + id);
if (!appeared.add(name)) {
LibJf.LOGGER.warn("Duplicate key in config for " + id + ": " + name);
continue;
}
actions.get(name).task.accept(reader);
}
reader.endObject();
actions.forEach((name, action) -> {
if (action.required && !appeared.contains(name)) {
LibJf.LOGGER.error("Missing entry in config for " + id + ": " + name);
}
});
} catch (IOException e) {
throw new JsonParseException("Could not read config", e);
}
@ -68,8 +70,8 @@ public class DefaultConfigIO {
Map<String, Action> actions = new HashMap<>();
category.getEntries().forEach(entry -> actions.putIfAbsent(entry.getName(), new Action(reader -> {
try {
entry.loadFromJson(JsonParser.parseReader(reader));
} catch (IllegalAccessException e) {
entry.loadFromJson(reader);
} catch (IllegalAccessException | IOException e) {
LibJf.LOGGER.error("Could not set config entry value of " + entry.getName(), e);
}
})));
@ -80,9 +82,7 @@ public class DefaultConfigIO {
});
if (category instanceof DslConfigCategory cat) {
cat.migrations.forEach((id, migration) -> {
actions.putIfAbsent(id, new Action(reader -> {
migration.accept(JsonParser.parseReader(reader));
}, false));
actions.putIfAbsent(id, new Action(migration, false));
});
}
return Map.copyOf(actions);

View File

@ -1,6 +1,6 @@
package io.gitlab.jfronny.libjf.config.impl.dsl;
import io.gitlab.jfronny.gson.JsonElement;
import io.gitlab.jfronny.gson.stream.JsonReader;
import io.gitlab.jfronny.libjf.config.api.v1.*;
import io.gitlab.jfronny.libjf.config.api.v1.dsl.CategoryBuilder;
import org.jetbrains.annotations.ApiStatus;
@ -17,7 +17,7 @@ public class DslConfigCategory implements ConfigCategory {
private final List<EntryInfo<?>> entries;
private final Map<String, Runnable> presets;
private final List<Supplier<List<ConfigInstance>>> referencedConfigs;
@ApiStatus.Internal public final Map<String, Consumer<JsonElement>> migrations;
@ApiStatus.Internal public final Map<String, Consumer<JsonReader>> migrations;
private final Map<String, ConfigCategory> categories;
private final Supplier<ConfigInstance> root;
private final List<Consumer<ConfigCategory>> verifiers;
@ -31,7 +31,7 @@ public class DslConfigCategory implements ConfigCategory {
List<CategoryBuilder<?>> categories,
Supplier<ConfigInstance> root,
List<Consumer<ConfigCategory>> verifiers,
Map<String, Consumer<JsonElement>> migrations) {
Map<String, Consumer<JsonReader>> migrations) {
this.id = id;
this.categoryPath = categoryPath;
this.translationPrefix = translationPrefix;

View File

@ -1,6 +1,6 @@
package io.gitlab.jfronny.libjf.config.impl.dsl;
import io.gitlab.jfronny.gson.JsonElement;
import io.gitlab.jfronny.gson.stream.JsonReader;
import io.gitlab.jfronny.libjf.config.api.v1.*;
import io.gitlab.jfronny.libjf.config.api.v1.dsl.CategoryBuilder;
import org.jetbrains.annotations.Nullable;
@ -23,7 +23,7 @@ public class DslConfigInstance extends DslConfigCategory implements ConfigInstan
List<CategoryBuilder<?>> categories,
Supplier<ConfigInstance> root,
List<Consumer<ConfigCategory>> verifiers,
Map<String, Consumer<JsonElement>> migrations,
Map<String, Consumer<JsonReader>> migrations,
Consumer<ConfigInstance> load,
Consumer<ConfigInstance> write,
@Nullable Path path) {

View File

@ -4,7 +4,7 @@ import io.gitlab.jfronny.commons.serialize.gson.api.v1.GsonHolders;
import io.gitlab.jfronny.commons.throwable.ThrowingConsumer;
import io.gitlab.jfronny.commons.throwable.ThrowingSupplier;
import io.gitlab.jfronny.gson.JsonElement;
import io.gitlab.jfronny.gson.stream.JsonWriter;
import io.gitlab.jfronny.gson.stream.*;
import io.gitlab.jfronny.libjf.LibJf;
import io.gitlab.jfronny.libjf.config.api.v1.Entry;
import io.gitlab.jfronny.libjf.config.api.v1.EntryInfo;
@ -128,38 +128,36 @@ public class DslEntryInfo<T> implements EntryInfo<T> {
}
@Override
public void loadFromJson(JsonElement element) throws IllegalAccessException {
public void loadFromJson(JsonReader reader) throws IOException, IllegalAccessException {
var next = reader.peek();
if (type.isBool()) {
if (element.isJsonPrimitive() && element.getAsJsonPrimitive().isBoolean()) {
setUnchecked(element.getAsBoolean());
}
if (next == JsonToken.BOOLEAN) setUnchecked(reader.nextBoolean());
else LibJf.LOGGER.error("Unexpected value for " + name + ": expected boolean but got " + next);
} else if (type.isString()) {
if (element.isJsonPrimitive()) {
setUnchecked(element.getAsString());
}
if (next == JsonToken.STRING || next == JsonToken.NUMBER) setUnchecked(reader.nextString());
else if (next == JsonToken.BOOLEAN) setUnchecked(Boolean.toString(reader.nextBoolean()));
else if (next == JsonToken.NULL) setUnchecked(null);
else LibJf.LOGGER.error("Unexpected value for " + name + ": expected string but got " + next);
} else if (type.isInt()) {
if (element.isJsonPrimitive() && element.getAsJsonPrimitive().isNumber()) {
setUnchecked(element.getAsNumber().intValue());
}
if (next == JsonToken.NUMBER) setUnchecked(reader.nextInt());
else LibJf.LOGGER.error("Unexpected value for " + name + ": expected number but got " + next);
} else if (type.isLong()) {
if (element.isJsonPrimitive() && element.getAsJsonPrimitive().isNumber()) {
setUnchecked(element.getAsNumber().longValue());
}
if (next == JsonToken.NUMBER) setUnchecked(reader.nextLong());
else LibJf.LOGGER.error("Unexpected value for " + name + ": expected number but got " + next);
} else if (type.isDouble()) {
if (element.isJsonPrimitive() && element.getAsJsonPrimitive().isNumber()) {
setUnchecked(element.getAsNumber().doubleValue());
if (next == JsonToken.NUMBER) {
setUnchecked(reader.nextDouble());
}
else LibJf.LOGGER.error("Unexpected value for " + name + ": expected number but got " + next);
} else if (type.isFloat()) {
if (element.isJsonPrimitive() && element.getAsJsonPrimitive().isNumber()) {
setUnchecked(element.getAsNumber().floatValue());
}
if (next == JsonToken.NUMBER) setUnchecked((float) reader.nextDouble());
else LibJf.LOGGER.error("Unexpected value for " + name + ": expected number but got " + next);
} else if (type.isEnum()) {
Type.TEnum<T> e = (Type.TEnum<T>) type;
if (element.isJsonPrimitive() && element.getAsJsonPrimitive().isString()) {
setUnchecked(e.optionForString(element.getAsString()));
}
if (next == JsonToken.STRING) setUnchecked(e.optionForString(reader.nextString()));
else LibJf.LOGGER.error("Unexpected value for " + name + ": expected string but got " + next);
} else {
setValue(GsonHolders.CONFIG.getGson().fromJson(element, type.asClass()));
setValue(GsonHolders.CONFIG.getGson().fromJson(reader, type.asClass()));
}
}

View File

@ -9,8 +9,8 @@ public class TestConfigTweaker {
public static ConfigBuilder<?> tweak(ConfigBuilder<?> builder) {
if (!Objects.equals("libjf-config-reflect-v1-testmod", builder.getId())) throw new IllegalStateException("No!");
LibJf.LOGGER.info("Called config tweaker");
return builder.addMigration("joe", jsonElement -> {
TestConfig.disablePacks = jsonElement.getAsJsonPrimitive().getAsBoolean();
return builder.addMigration("joe", reader -> {
TestConfig.disablePacks = reader.nextBoolean();
});
}
}