Running with Redirects
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'com.gradleup.shadow' version '8.3.5'
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven {
|
||||
url = "https://libraries.minecraft.net"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly "com.mojang:brigadier:${brigadier_version}"
|
||||
compileOnly "net.kyori:adventure-api:${adventure_version}"
|
||||
compileOnly "net.kyori:adventure-text-serializer-json:${adventure_version}"
|
||||
compileOnly "net.kyori:adventure-text-minimessage:${adventure_version}"
|
||||
|
||||
compileOnly("org.slf4j:slf4j-api:$slf4j_version")
|
||||
|
||||
compileOnly("com.google.code.gson:gson:${gson_version}")
|
||||
|
||||
implementation "de.themoep:minedown-adventure:${minedown_version}"
|
||||
}
|
||||
|
||||
def targetJavaVersion = 21
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
// ensure that the encoding is set to UTF-8, no matter what the system default is
|
||||
// this fixes some edge cases with special characters not displaying correctly
|
||||
// see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html
|
||||
// If Javadoc is generated, this must be specified in that task too.
|
||||
it.options.encoding = "UTF-8"
|
||||
if (targetJavaVersion >= 10 || JavaVersion.current().isJava10Compatible()) {
|
||||
it.options.release.set(targetJavaVersion)
|
||||
}
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
relocate("de.themoep.minedown.adventure", "io.github.skippyall.minedown")
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package io.github.skippyall.ruler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class FileUtils {
|
||||
public static Path removeExtension(Path path) {
|
||||
return Path.of(removeExtension(path.toString()));
|
||||
}
|
||||
|
||||
public static String removeExtension(String path) {
|
||||
if(path.contains(".")) {
|
||||
return path.substring(0, path.lastIndexOf('.'));
|
||||
} else {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getExtension(Path path) {
|
||||
return getExtension(path.toString());
|
||||
}
|
||||
|
||||
public static String getExtension(String path) {
|
||||
if(path.contains(".")) {
|
||||
return path.substring(path.lastIndexOf('.') + 1);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public static Path append(Path path, String extension) {
|
||||
return Path.of(path.toString() + extension);
|
||||
}
|
||||
|
||||
public static Path findFileWithName(Path path) {
|
||||
try (Stream<Path> files = Files.list(path.getParent())) {
|
||||
return files
|
||||
.filter(foundPath -> removeExtension(foundPath).equals(path))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package io.github.skippyall.ruler;
|
||||
|
||||
import de.themoep.minedown.adventure.MineDown;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.ComponentSerializer;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
|
||||
public class MineDownSerializer implements ComponentSerializer<Component, Component, String> {
|
||||
@Override
|
||||
public @NonNull Component deserialize(@NonNull String input) {
|
||||
return MineDown.parse(input);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull String serialize(@NonNull Component component) {
|
||||
return MineDown.stringify(component);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package io.github.skippyall.ruler;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public interface Platform {
|
||||
Path getConfigDirectory();
|
||||
Logger getLogger();
|
||||
<T> CompletableFuture<T> runAsync(Supplier<T> supplier);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package io.github.skippyall.ruler;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class Ruler {
|
||||
public static final String ID = "ruler";
|
||||
|
||||
@Nullable
|
||||
private static Platform platform;
|
||||
|
||||
public static void init(Platform platform) {
|
||||
Ruler.platform = platform;
|
||||
}
|
||||
|
||||
public static Platform getPlatform() {
|
||||
if(platform == null) {
|
||||
throw new IllegalStateException("Ruler is not initialized yet!");
|
||||
}
|
||||
return platform;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package io.github.skippyall.ruler.command;
|
||||
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
|
||||
public interface CommandHelper<S> {
|
||||
Audience toAudience(S source);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package io.github.skippyall.ruler.command;
|
||||
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
import io.github.skippyall.ruler.Ruler;
|
||||
import io.github.skippyall.ruler.config.ConfigLoader;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class Commands<S> {
|
||||
private final CommandHelper<S> helper;
|
||||
private final RuleSuggestionProvider<S> ruleSuggestionProvider = new RuleSuggestionProvider<>();
|
||||
|
||||
public Commands(CommandHelper<S> helper) {
|
||||
this.helper = helper;
|
||||
}
|
||||
|
||||
public List<LiteralArgumentBuilder<S>> getCommands() {
|
||||
return List.of(
|
||||
literal("ruler")
|
||||
.then(literal("open")
|
||||
.then(argument("path", StringArgumentType.string())
|
||||
.suggests(ruleSuggestionProvider)
|
||||
.executes(context -> {
|
||||
String path = StringArgumentType.getString(context, "path");
|
||||
Audience player = helper.toAudience(context.getSource());
|
||||
|
||||
try {
|
||||
player.sendMessage(ConfigLoader.getRule(path, player));
|
||||
} catch (Exception e) {
|
||||
player.sendMessage(Component.text("This file doesn't exist").color(NamedTextColor.RED));
|
||||
Ruler.getPlatform().getLogger().error("Error while loading rule", e);
|
||||
}
|
||||
return 0;
|
||||
})
|
||||
)
|
||||
)
|
||||
.executes(context -> {
|
||||
helper.toAudience(context.getSource()).sendMessage(Component.text("Hi"));
|
||||
return 0;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private LiteralArgumentBuilder<S> literal(String name) {
|
||||
return LiteralArgumentBuilder.literal(name);
|
||||
}
|
||||
|
||||
private <T> RequiredArgumentBuilder<S, T> argument(String name, ArgumentType<T> type) {
|
||||
return RequiredArgumentBuilder.argument(name, type);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package io.github.skippyall.ruler.command;
|
||||
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.suggestion.SuggestionProvider;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
import io.github.skippyall.ruler.Ruler;
|
||||
import io.github.skippyall.ruler.rule.RuleUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class RuleSuggestionProvider<S> implements SuggestionProvider<S> {
|
||||
@Override
|
||||
public CompletableFuture<Suggestions> getSuggestions(CommandContext<S> commandContext, SuggestionsBuilder suggestionsBuilder) throws CommandSyntaxException {
|
||||
return Ruler.getPlatform().runAsync(() -> {
|
||||
String start = suggestionsBuilder.getInput();
|
||||
int last = start.lastIndexOf('.');
|
||||
|
||||
String incomplete;
|
||||
String completed = "";
|
||||
|
||||
if (last != -1) {
|
||||
incomplete = start.substring(last + 1);
|
||||
completed = start.substring(0, last);
|
||||
} else {
|
||||
incomplete = start;
|
||||
}
|
||||
List<String> rules = RuleUtils.listRules(completed, true);
|
||||
for (String rule : rules) {
|
||||
if (rule.startsWith(incomplete)) {
|
||||
suggestionsBuilder.suggest(completed + rule);
|
||||
}
|
||||
}
|
||||
return suggestionsBuilder.build();
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package io.github.skippyall.ruler.config;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonParseException;
|
||||
import io.github.skippyall.ruler.FileUtils;
|
||||
import io.github.skippyall.ruler.Ruler;
|
||||
import io.github.skippyall.ruler.config.condition.Condition;
|
||||
import io.github.skippyall.ruler.config.condition.ConditionTypeAdapter;
|
||||
import io.github.skippyall.ruler.rule.InvalidRuleException;
|
||||
import io.github.skippyall.ruler.rule.RuleUtils;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.ComponentSerializer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class ConfigLoader {
|
||||
public static final Gson configSerializer = new GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.registerTypeAdapter(Condition.class, new ConditionTypeAdapter())
|
||||
.registerTypeAdapter(Redirect.class, new Redirect.Adapter())
|
||||
.create();
|
||||
|
||||
public static RuleConfig loadConfig(String rulePath) {
|
||||
Path configPath = RuleUtils.getFilePathWithoutExtension(rulePath);
|
||||
return loadConfig(configPath);
|
||||
}
|
||||
|
||||
private static RuleConfig loadConfig(Path fileWithoutExtension) {
|
||||
RuleConfig parentConfig = null;
|
||||
if(RuleUtils.isInDirectory(fileWithoutExtension.getParent())) {
|
||||
parentConfig = loadConfig(fileWithoutExtension.getParent());
|
||||
}
|
||||
RuleConfig config = null;
|
||||
Path configPath = FileUtils.append(fileWithoutExtension, ".config.json");
|
||||
if(Files.isRegularFile(configPath)) {
|
||||
try {
|
||||
config = configSerializer.fromJson(Files.newBufferedReader(configPath), RuleConfig.class);
|
||||
} catch (IOException | JsonParseException e) {
|
||||
Ruler.getPlatform().getLogger().error("Could not read config {}", configPath, e);
|
||||
return parentConfig;
|
||||
}
|
||||
} else {
|
||||
return parentConfig;
|
||||
}
|
||||
|
||||
if(parentConfig != null) {
|
||||
config.applyParentRules(parentConfig);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
public static String getActualRulePath(String rulePath, Audience audience) {
|
||||
Path fileWithoutExtension = RuleUtils.getFilePathWithoutExtension(rulePath);
|
||||
RuleConfig config = loadConfig(fileWithoutExtension);
|
||||
|
||||
if(config != null) {
|
||||
for (Redirect redirect : config.getRedirects()) {
|
||||
if (redirect.getCondition().test(audience)) {
|
||||
return redirect.getRulePath();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rulePath;
|
||||
}
|
||||
|
||||
public static Component getRule(String path, Audience player) throws InvalidRuleException {
|
||||
String actualPath = ConfigLoader.getActualRulePath(path, player);
|
||||
Ruler.getPlatform().getLogger().error(actualPath);
|
||||
|
||||
Path filePath = RuleUtils.getFilePath(actualPath);
|
||||
|
||||
String extension = FileUtils.getExtension(filePath);
|
||||
|
||||
ComponentSerializer<Component, Component, String> serializer = RuleUtils.getSerializer(extension);
|
||||
|
||||
if(serializer == null) {
|
||||
throw new InvalidRuleException("File doesn't exist");
|
||||
}
|
||||
|
||||
|
||||
return RuleUtils.readRuleFromFile(filePath, serializer);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package io.github.skippyall.ruler.config;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
import io.github.skippyall.ruler.config.condition.AlwaysCondition;
|
||||
import io.github.skippyall.ruler.config.condition.Condition;
|
||||
import io.github.skippyall.ruler.config.condition.Conditions;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
public class Redirect {
|
||||
Condition condition = AlwaysCondition.ALWAYS_TRUE;
|
||||
String rulePath;
|
||||
|
||||
public Redirect(String rulePath) {
|
||||
this.rulePath = rulePath;
|
||||
}
|
||||
|
||||
public Redirect(Condition condition, String rulePath) {
|
||||
this.condition = condition;
|
||||
this.rulePath = rulePath;
|
||||
}
|
||||
|
||||
public Condition getCondition() {
|
||||
return condition;
|
||||
}
|
||||
|
||||
public void setCondition(Condition condition) {
|
||||
this.condition = condition;
|
||||
}
|
||||
|
||||
public String getRulePath() {
|
||||
return rulePath;
|
||||
}
|
||||
|
||||
public void setRulePath(String rulePath) {
|
||||
this.rulePath = rulePath;
|
||||
}
|
||||
|
||||
public static class Adapter implements JsonDeserializer<Redirect>, JsonSerializer<Redirect> {
|
||||
@Override
|
||||
public Redirect deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||
if(json.isJsonObject()) {
|
||||
JsonObject object = json.getAsJsonObject();
|
||||
String rulePath = object.get("rulePath").getAsString();
|
||||
Condition condition = AlwaysCondition.ALWAYS_TRUE;
|
||||
if(object.has("condition")) {
|
||||
condition = Conditions.deserialize(object.get("condition"));
|
||||
}
|
||||
return new Redirect(condition, rulePath);
|
||||
} else {
|
||||
String rulePath = json.getAsString();
|
||||
return new Redirect(rulePath);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement serialize(Redirect src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
JsonObject object = new JsonObject();
|
||||
object.addProperty("rulePath", src.rulePath);
|
||||
object.add("condition", Conditions.serialize(src.condition));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package io.github.skippyall.ruler.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class RuleConfig {
|
||||
private List<Redirect> redirects = new ArrayList<>();
|
||||
|
||||
public RuleConfig() {
|
||||
|
||||
}
|
||||
|
||||
public void applyParentRules(RuleConfig parent) {
|
||||
redirects.addAll(parent.getRedirects());
|
||||
}
|
||||
|
||||
public List<Redirect> getRedirects() {
|
||||
return redirects;
|
||||
}
|
||||
|
||||
public void setRedirects(List<Redirect> redirects) {
|
||||
this.redirects = redirects;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package io.github.skippyall.ruler.config.condition;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import io.github.skippyall.ruler.Ruler;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.key.Key;
|
||||
|
||||
public class AlwaysCondition implements Condition {
|
||||
public static final Key ALWAYS_TRUE_KEY = Key.key(Ruler.ID, "always_true");
|
||||
public static final Key ALWAYS_FALSE_KEY = Key.key(Ruler.ID, "always_false");
|
||||
|
||||
public static final AlwaysCondition ALWAYS_TRUE = new AlwaysCondition(true, ALWAYS_TRUE_KEY);
|
||||
public static final AlwaysCondition ALWAYS_FALSE = new AlwaysCondition(false, ALWAYS_FALSE_KEY);
|
||||
|
||||
private final boolean value;
|
||||
private final Key type;
|
||||
|
||||
private AlwaysCondition(boolean value, Key type) {
|
||||
this.value = value;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(Audience audience) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonObject serialize() {
|
||||
return new JsonObject();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package io.github.skippyall.ruler.config.condition;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.key.Key;
|
||||
|
||||
public interface Condition {
|
||||
boolean test(Audience audience);
|
||||
|
||||
Key getType();
|
||||
|
||||
JsonObject serialize();
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package io.github.skippyall.ruler.config.condition;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
public class ConditionTypeAdapter implements JsonSerializer<Condition>, JsonDeserializer<Condition> {
|
||||
@Override
|
||||
public Condition deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||
try {
|
||||
return Conditions.deserialize(json);
|
||||
} catch (Exception e) {
|
||||
throw new JsonParseException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement serialize(Condition src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
return Conditions.serialize(src);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package io.github.skippyall.ruler.config.condition;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import net.kyori.adventure.key.Key;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class Conditions {
|
||||
private static final HashMap<Key, Function<JsonObject, Condition>> deserializers = new HashMap<>();
|
||||
|
||||
public static void register(Key name, Function<JsonObject, Condition> deserializer) {
|
||||
deserializers.put(name, deserializer);
|
||||
}
|
||||
|
||||
public static Condition deserialize(JsonElement config) {
|
||||
if(config.isJsonObject()) {
|
||||
JsonObject object = config.getAsJsonObject();
|
||||
return deserializers.get(Key.key(object.get("type").getAsString())).apply(object);
|
||||
} else {
|
||||
String type = config.getAsString();
|
||||
return deserializers.get(Key.key(type)).apply(null);
|
||||
}
|
||||
}
|
||||
|
||||
public static JsonObject serialize(Condition condition) {
|
||||
JsonObject config = condition.serialize();
|
||||
config.addProperty("type", condition.getType().asString());
|
||||
return config;
|
||||
}
|
||||
|
||||
static {
|
||||
register(AlwaysCondition.ALWAYS_TRUE_KEY, object -> AlwaysCondition.ALWAYS_TRUE);
|
||||
register(AlwaysCondition.ALWAYS_FALSE_KEY, object -> AlwaysCondition.ALWAYS_FALSE);
|
||||
register(TwoParamLogicCondition.AND_KEY, element -> TwoParamLogicCondition.and(TwoParamLogicCondition.deserialize(element)));
|
||||
register(TwoParamLogicCondition.OR_KEY, element -> TwoParamLogicCondition.or(TwoParamLogicCondition.deserialize(element)));
|
||||
}
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
package io.github.skippyall.ruler.config.condition;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import io.github.skippyall.ruler.Ruler;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.key.Key;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.BinaryOperator;
|
||||
|
||||
public class TwoParamLogicCondition implements Condition {
|
||||
public static final Key AND_KEY = Key.key(Ruler.ID, "and");
|
||||
public static final Key OR_KEY = Key.key(Ruler.ID, "or");
|
||||
|
||||
private final Key key;
|
||||
private final BinaryOperator<Boolean> logic;
|
||||
private List<Condition> conditions;
|
||||
|
||||
private TwoParamLogicCondition(Key key, BinaryOperator<Boolean> logic, List<Condition> conditions) {
|
||||
this.key = key;
|
||||
this.logic = logic;
|
||||
this.conditions = conditions;
|
||||
}
|
||||
|
||||
public static TwoParamLogicCondition and(List<Condition> conditions) {
|
||||
return new TwoParamLogicCondition(AND_KEY, (b1, b2) -> b1 && b2, conditions);
|
||||
}
|
||||
|
||||
public static TwoParamLogicCondition or(List<Condition> conditions) {
|
||||
return new TwoParamLogicCondition(OR_KEY, (b1, b2) -> b1 || b2, conditions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(Audience audience) {
|
||||
boolean currentValue = false;
|
||||
boolean first = true;
|
||||
|
||||
for(Condition condition : conditions) {
|
||||
if(first) {
|
||||
currentValue = condition.test(audience);
|
||||
first = false;
|
||||
} else {
|
||||
currentValue = logic.apply(currentValue, condition.test(audience));
|
||||
}
|
||||
}
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key getType() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonObject serialize() {
|
||||
JsonArray conditionArray = new JsonArray();
|
||||
for(Condition condition : conditions) {
|
||||
conditionArray.add(Conditions.serialize(condition));
|
||||
}
|
||||
JsonObject object = new JsonObject();
|
||||
object.add("conditions", conditionArray);
|
||||
return object;
|
||||
}
|
||||
|
||||
public static List<Condition> deserialize(JsonElement element) {
|
||||
List<Condition> conditions = new ArrayList<>();
|
||||
JsonArray array = element.getAsJsonArray();
|
||||
for(JsonElement conditionElement : array) {
|
||||
conditions.add(Conditions.deserialize(conditionElement.getAsJsonObject()));
|
||||
}
|
||||
return conditions;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package io.github.skippyall.ruler.rule;
|
||||
|
||||
public class InvalidRuleException extends RuntimeException {
|
||||
public InvalidRuleException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public InvalidRuleException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public InvalidRuleException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
package io.github.skippyall.ruler.rule;
|
||||
|
||||
import io.github.skippyall.ruler.FileUtils;
|
||||
import io.github.skippyall.ruler.MineDownSerializer;
|
||||
import io.github.skippyall.ruler.Ruler;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.serializer.ComponentSerializer;
|
||||
import net.kyori.adventure.text.serializer.json.JSONComponentSerializer;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class RuleUtils {
|
||||
private static final Map<String, ComponentSerializer<Component, Component, String>> extension_to_serializer = Map.of(
|
||||
"json", JSONComponentSerializer.json(),
|
||||
"md", new MineDownSerializer(),
|
||||
"minimessage", MiniMessage.miniMessage()
|
||||
);
|
||||
|
||||
@Nullable
|
||||
public static ComponentSerializer<Component, Component, String> getSerializer(String fileNameExtension) {
|
||||
return extension_to_serializer.get(fileNameExtension);
|
||||
}
|
||||
|
||||
public static Path getRoot() {
|
||||
return Ruler.getPlatform().getConfigDirectory().resolve("rules").toAbsolutePath();
|
||||
}
|
||||
|
||||
public static boolean isValidRulePath(String path) {
|
||||
return path.matches("([A-Za-z0-9\\-_.]+)") &&
|
||||
Arrays.stream(path.split("\\.")).noneMatch(String::isEmpty);
|
||||
}
|
||||
|
||||
public static void checkValidRulePath(String path) throws InvalidRuleException {
|
||||
if(!isValidRulePath(path)) {
|
||||
throw new InvalidRuleException("\"" + path + "\" is not a valid rule path");
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isInDirectory(Path path) {
|
||||
return path.toAbsolutePath().startsWith(getRoot());
|
||||
}
|
||||
|
||||
public static void checkDirectory(Path path) throws InvalidRuleException {
|
||||
if(!isInDirectory(path)) {
|
||||
throw new InvalidRuleException("File path \"" + path.toString() + "\" is not in the root rule directory.");
|
||||
}
|
||||
}
|
||||
|
||||
public static Path getFilePathWithoutExtension(String rulePath) throws InvalidRuleException {
|
||||
if(rulePath.isEmpty()) {
|
||||
return getRoot();
|
||||
}
|
||||
checkValidRulePath(rulePath);
|
||||
Path joinedPath = getRoot().resolve(rulePath.replace(".", FileSystems.getDefault().getSeparator())).toAbsolutePath();
|
||||
checkDirectory(joinedPath);
|
||||
return joinedPath;
|
||||
}
|
||||
|
||||
public static Path getFilePath(String rulePath) throws InvalidRuleException {
|
||||
Path withoutExtension = getFilePathWithoutExtension(rulePath);
|
||||
|
||||
String extension = tryFormats(withoutExtension);
|
||||
if(extension == null) {
|
||||
throw new InvalidRuleException("File for rule \"" + rulePath + "\" not found.");
|
||||
}
|
||||
|
||||
return Path.of(withoutExtension.toString() + "." + extension);
|
||||
}
|
||||
|
||||
public static String getRulePath(Path filePath) throws InvalidRuleException {
|
||||
checkDirectory(filePath);
|
||||
|
||||
Path relative = filePath.toAbsolutePath().relativize(getRoot());
|
||||
String withoutExtension = FileUtils.removeExtension(relative).toString();
|
||||
String path = withoutExtension.replace(FileSystems.getDefault().getSeparator(), ".");
|
||||
|
||||
checkValidRulePath(path);
|
||||
return path;
|
||||
}
|
||||
|
||||
public static Component readRuleFromFile(Path path, ComponentSerializer<Component, Component, String> serializer) throws InvalidRuleException {
|
||||
String text;
|
||||
try {
|
||||
text = Files.readString(path);
|
||||
} catch (IOException e) {
|
||||
throw new InvalidRuleException("Exception while reading file", e);
|
||||
}
|
||||
try {
|
||||
return serializer.deserialize(text);
|
||||
} catch (Exception e) {
|
||||
throw new InvalidRuleException("Exception while parsing", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static @Nullable String tryFormats(Path path) {
|
||||
String stringPath = path.toString();
|
||||
for (String extension : extension_to_serializer.keySet()) {
|
||||
if(Files.exists(Path.of(stringPath + "." + extension))) {
|
||||
return extension;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static List<String> listRules(String basePath, boolean directories) {
|
||||
Path dirPath = getFilePathWithoutExtension(basePath);
|
||||
try {
|
||||
if(!Files.isDirectory(dirPath)) {
|
||||
return List.of();
|
||||
}
|
||||
try(Stream<Path> paths = Files.list(dirPath)) {
|
||||
return paths
|
||||
.filter(path -> {
|
||||
if (Files.isDirectory(path)) {
|
||||
return directories;
|
||||
} else {
|
||||
return extension_to_serializer.containsKey(FileUtils.getExtension(path));
|
||||
}
|
||||
})
|
||||
.map(RuleUtils::getRulePath)
|
||||
.toList();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Ruler.getPlatform().getLogger().error("Exception while listing rules", e);
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user