Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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 CliExtensibleRegistry {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like the name "cli extensible registry" could definitely be workshopped a bit, but that's a personal nitpick

/**
* 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 + ".searchPath")),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

personally I am partial to using kebab case for something like this rather than camel case, but this is a personal nitpick and it's honestly fine either way

Stream.of(baseSearchPath)
);

Stream<Path> extraPath = parseValidPaths(System.getProperty(basePropertyName + ".extraPath"));
Comment on lines +57 to +61
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need both extra path & search path, or would only one be sufficient?


return Stream.concat(
searchPath
.filter(Files::isDirectory)
.flatMap(CliExtensibleRegistry::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();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,14 @@
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.CliExtensibleRegistry;
import com.dfsek.terra.registry.OpenRegistryImpl;


/**
* Class to hold config packs
*/
public class ConfigRegistry extends OpenRegistryImpl<ConfigPack> {
public class ConfigRegistry extends OpenRegistryImpl<ConfigPack> implements CliExtensibleRegistry {

public ConfigRegistry() {
super(TypeKey.of(ConfigPack.class));
Expand All @@ -45,7 +46,7 @@ public synchronized void loadAll(Platform platform) throws IOException, PackLoad
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);
Expand All @@ -60,6 +61,17 @@ public synchronized void loadAll(Platform platform) throws IOException, PackLoad
}
}

@Override
public String getRegistryName() {
return "config";
}

@Override
public boolean validatePathIsMember(Path path) {
var file = path.toFile();
return file.isDirectory() || file.getName().endsWith(".zip");
Comment on lines +71 to +72
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use the NIO Files api
with the files api, it allows it to easy be able to handle, for example, paths inside zip files (if we ever do support that) or something

}

public static class PackLoadFailuresException extends Exception {
@Serial
private static final long serialVersionUID = 538998844645186306L;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,15 @@
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.CliExtensibleRegistry;
import com.dfsek.terra.registry.OpenRegistryImpl;
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 CliExtensibleRegistry {

public MetaConfigRegistry() {
super(TypeKey.of(MetaPack.class));
Expand All @@ -45,8 +46,8 @@ public void loadAll(Platform platform, ConfigRegistry configRegistry) throws IOE
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);
Expand All @@ -59,4 +60,15 @@ public void loadAll(Platform platform, ConfigRegistry configRegistry) throws IOE
throw new PackLoadFailuresException(failedLoads);
}
}

@Override
public String getRegistryName() {
return "metaconfig";
}

@Override
public boolean validatePathIsMember(Path path) {
var file = path.toFile();
return file.isDirectory() || file.getName().endsWith(".zip");
Comment on lines +71 to +72
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIO Files api

}
}
Loading