Merge pull request #560 from everbuild-org/feat/extend-search-path

feat: add extensible registry interface and integrate into config and meta-config registries
This commit is contained in:
Zoë Gidiere
2026-09-28 10:35:59 +00:00
committed by GitHub
3 changed files with 120 additions and 5 deletions
@@ -0,0 +1,93 @@
package com.dfsek.terra.registry;
import com.google.errorprone.annotations.MustBeClosed;
import org.intellij.lang.annotations.Pattern;
import org.jspecify.annotations.Nullable;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Objects;
import java.util.stream.Stream;
/**
* A file-discoverable registry that can be extended by system properties
* <br>
* The main use-case for this is to allow for customized locations of file-system-based key entries,
* such as putting config packs into custom locations.
* <br>
* Two system properties are used to define the search path:
* <br>
* <code>terra.registry.{searchPathName}.searchPath</code> is a list of "parent folder" paths (like the pack directory) that
* will be searched for contained files.
* <br>
* <code>terra.registry.{searchPathName}.extraPath</code> is a list of files that will be directly evaluated for membership and
* as such will be directly treated as members of the registry.
* <br>
* These system properties are formatted like UNIX-style paths, with each path separated by a colon (':') character.
*/
public interface ExtensibleRegistry {
/**
* Get a lowercase search path name for this registry that will get plugged into
* <br>
* <code>terra.registry.{searchPathName}.searchPath</code>
* <br>
* and
* <br>
* <code>terra.registry.{searchPathName}.extraPath</code>
*/
@Pattern("^[a-z._]+$")
String getRegistryName();
/**
* Check if a path may be a member of this registry heuristically.
*
* @param path Path to check
*
* @return True if the path is a member of this registry.
*/
boolean validatePathIsMember(Path path);
default @MustBeClosed Stream<Path> getMemberPaths(Path baseSearchPath) {
String basePropertyName = "terra.registry." + getRegistryName();
Stream<Path> searchPath = Stream.concat(
parseValidPaths(System.getProperty(basePropertyName + ".search-path")),
Stream.of(baseSearchPath)
);
Stream<Path> extraPath = parseValidPaths(System.getProperty(basePropertyName + ".extra-path"));
return Stream.concat(
searchPath
.filter(Files::isDirectory)
.flatMap(ExtensibleRegistry::listDirectory),
extraPath
)
.filter(this::validatePathIsMember);
}
private static Stream<Path> parseValidPaths(@Nullable String paths) {
return Stream.ofNullable(paths)
.flatMap(p -> Arrays.stream(p.split(":")))
.filter(v -> !v.isBlank())
.map(v -> {
try {
return Path.of(v).toAbsolutePath().normalize();
} catch (Exception e) {
return null;
}
})
.filter(Objects::nonNull);
}
private static Stream<Path> listDirectory(Path dir) {
try {
return Files.list(dir);
} catch(IOException e) {
return Stream.empty();
}
}
}
@@ -29,13 +29,14 @@ import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.util.reflection.TypeKey;
import com.dfsek.terra.config.pack.ConfigPackImpl;
import com.dfsek.terra.registry.ExtensibleRegistry;
import com.dfsek.terra.registry.OpenRegistryImpl;
/**
* Class to hold config packs
*/
public class ConfigRegistry extends OpenRegistryImpl<ConfigPack> {
public class ConfigRegistry extends OpenRegistryImpl<ConfigPack> implements ExtensibleRegistry {
public ConfigRegistry() {
super(TypeKey.of(ConfigPack.class));
@@ -45,7 +46,7 @@ public class ConfigRegistry extends OpenRegistryImpl<ConfigPack> {
Path packsDirectory = platform.getDataFolder().toPath().resolve("packs");
Files.createDirectories(packsDirectory);
List<Exception> failedLoads = new CopyOnWriteArrayList<>();
try(Stream<Path> packs = Files.list(packsDirectory)) {
try(Stream<Path> packs = getMemberPaths(packsDirectory)) {
packs.parallel().forEach(path -> {
try {
ConfigPack pack = new ConfigPackImpl(path, platform);
@@ -60,6 +61,16 @@ public class ConfigRegistry extends OpenRegistryImpl<ConfigPack> {
}
}
@Override
public String getRegistryName() {
return "config";
}
@Override
public boolean validatePathIsMember(Path path) {
return Files.isDirectory(path)|| path.getFileName().endsWith(".zip");
}
public static class PackLoadFailuresException extends Exception {
@Serial
private static final long serialVersionUID = 538998844645186306L;
@@ -28,6 +28,7 @@ import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.config.MetaPack;
import com.dfsek.terra.api.util.reflection.TypeKey;
import com.dfsek.terra.config.pack.MetaPackImpl;
import com.dfsek.terra.registry.ExtensibleRegistry;
import com.dfsek.terra.registry.OpenRegistryImpl;
import com.dfsek.terra.registry.master.ConfigRegistry.PackLoadFailuresException;
@@ -35,7 +36,7 @@ import com.dfsek.terra.registry.master.ConfigRegistry.PackLoadFailuresException;
/**
* Class to hold config packs
*/
public class MetaConfigRegistry extends OpenRegistryImpl<MetaPack> {
public class MetaConfigRegistry extends OpenRegistryImpl<MetaPack> implements ExtensibleRegistry {
public MetaConfigRegistry() {
super(TypeKey.of(MetaPack.class));
@@ -45,8 +46,8 @@ public class MetaConfigRegistry extends OpenRegistryImpl<MetaPack> {
Path packsDirectory = platform.getDataFolder().toPath().resolve("metapacks");
Files.createDirectories(packsDirectory);
List<IOException> failedLoads = new ArrayList<>();
try(Stream<Path> packs = Files.list(packsDirectory)) {
packs.forEach(path -> {
try(Stream<Path> paths = getMemberPaths(packsDirectory)) {
paths.forEach(path -> {
try {
MetaPack pack = new MetaPackImpl(path, platform, configRegistry);
registerChecked(pack.getRegistryKey(), pack);
@@ -59,4 +60,14 @@ public class MetaConfigRegistry extends OpenRegistryImpl<MetaPack> {
throw new PackLoadFailuresException(failedLoads);
}
}
@Override
public String getRegistryName() {
return "metaconfig";
}
@Override
public boolean validatePathIsMember(Path path) {
return Files.isDirectory(path) || path.getFileName().endsWith(".zip");
}
}