Merge pull request 'Validate mod files' (#9) from TheKodeToad/better-mod-file-validation into main
All checks were successful
Publish to snapshot maven / build (push) Successful in 20s
All checks were successful
Publish to snapshot maven / build (push) Successful in 20s
Reviewed-on: #9 Reviewed-by: Ecorous <ecorous@outlook.com> Reviewed-by: owlsys <owlsys@noreply.localhost>
This commit is contained in:
commit
b957330656
|
@ -7,6 +7,7 @@ import java.nio.file.FileSystem;
|
||||||
import java.nio.file.FileSystems;
|
import java.nio.file.FileSystems;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import com.electronwill.nightconfig.core.CommentedConfig;
|
import com.electronwill.nightconfig.core.CommentedConfig;
|
||||||
|
@ -16,7 +17,10 @@ import com.electronwill.nightconfig.toml.TomlParser;
|
||||||
import dev.frogmc.frogloader.api.mod.ModDependencies;
|
import dev.frogmc.frogloader.api.mod.ModDependencies;
|
||||||
import dev.frogmc.frogloader.api.mod.ModExtensions;
|
import dev.frogmc.frogloader.api.mod.ModExtensions;
|
||||||
import dev.frogmc.frogloader.api.mod.ModProperties;
|
import dev.frogmc.frogloader.api.mod.ModProperties;
|
||||||
|
import dev.frogmc.frogloader.api.mod.SemVer;
|
||||||
|
import dev.frogmc.frogloader.impl.SemVerParseException;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -32,7 +36,7 @@ public class ModPropertiesReader {
|
||||||
CommentedConfig props = PARSER.parse(fs.getPath(PROPERTIES_FILE_NAME), FileNotFoundAction.THROW_ERROR);
|
CommentedConfig props = PARSER.parse(fs.getPath(PROPERTIES_FILE_NAME), FileNotFoundAction.THROW_ERROR);
|
||||||
|
|
||||||
return Optional.of(readProperties(props, Collections.singleton(mod)));
|
return Optional.of(readProperties(props, Collections.singleton(mod)));
|
||||||
} catch (IOException e) {
|
} catch (IOException | InvalidModPropertiesException e) {
|
||||||
LOGGER.warn("Failed to read mod properties: ", e);
|
LOGGER.warn("Failed to read mod properties: ", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,7 +64,7 @@ public class ModPropertiesReader {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ModProperties readProperties(UnmodifiableConfig config, Collection<Path> sources) {
|
private static ModProperties readProperties(UnmodifiableConfig config, Collection<Path> sources) throws InvalidModPropertiesException {
|
||||||
String manifestVer = config.get("frog.format_version");
|
String manifestVer = config.get("frog.format_version");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -73,53 +77,91 @@ public class ModPropertiesReader {
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
private enum FormatVersion {
|
private enum FormatVersion {
|
||||||
V1_0_0("1.0.0", (config, sources) -> {
|
V1_0_0("1.0.0", (config, sources) -> {
|
||||||
|
List<String> badProperties = new ArrayList<>();
|
||||||
|
|
||||||
String id = config.get("frog.mod.id");
|
String id = config.get("frog.mod.id");
|
||||||
String name = config.get("frog.mod.name");
|
String name = config.get("frog.mod.name");
|
||||||
String icon = config.get("frog.mod.icon");
|
String icon = config.get("frog.mod.icon");
|
||||||
String version = config.get("frog.mod.version");
|
String version = config.get("frog.mod.version");
|
||||||
String license = config.get("frog.mod.license");
|
String license = Objects.requireNonNullElse(config.get("frog.mod.license"), "");
|
||||||
|
|
||||||
if (license == null) {
|
if (id == null || id.isEmpty())
|
||||||
license = "";
|
badProperties.add("frog.mod.id");
|
||||||
|
|
||||||
|
if (name == null || name.isEmpty())
|
||||||
|
badProperties.add("frog.mod.name");
|
||||||
|
|
||||||
|
SemVer semVer = null;
|
||||||
|
|
||||||
|
if (version == null || version.isEmpty())
|
||||||
|
badProperties.add("frog.mod.version");
|
||||||
|
else {
|
||||||
|
try {
|
||||||
|
semVer = SemVerImpl.parse(version);
|
||||||
|
} catch (SemVerParseException e) {
|
||||||
|
badProperties.add("frog.mod.version");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<UnmodifiableConfig> creditsList = config.get("frog.mod.credits");
|
List<UnmodifiableConfig> creditsList = config.get("frog.mod.credits");
|
||||||
Map<String, Collection<String>> credits = new HashMap<>();
|
Map<String, Collection<String>> credits = new HashMap<>();
|
||||||
if (creditsList != null) {
|
if (creditsList != null) {
|
||||||
creditsList.forEach(c -> credits.put(c.get("name"), c.get("roles")));
|
int i = 0;
|
||||||
|
|
||||||
|
for (UnmodifiableConfig entry : creditsList) {
|
||||||
|
String entryName = entry.get("name");
|
||||||
|
Collection<String> entryRoles = entry.get("roles");
|
||||||
|
|
||||||
|
if (entryName == null || entryName.isEmpty())
|
||||||
|
badProperties.add("frog.mod.credits." + i + ".name");
|
||||||
|
|
||||||
|
if (entryRoles == null || entryRoles.isEmpty())
|
||||||
|
badProperties.add("frog.mod.credits." + i + ".name");
|
||||||
|
|
||||||
|
credits.put(entryName, entryRoles);
|
||||||
|
|
||||||
|
++i;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Collection<ModDependencies.Entry> depends = new HashSet<>();
|
Function<String, Collection<ModDependencies.Entry>> parseDependencies = key -> {
|
||||||
List<UnmodifiableConfig> dependsConfig = config.get("frog.dependencies.depends");
|
Collection<ModDependencies.Entry> result = new HashSet<>();
|
||||||
if (dependsConfig != null) {
|
|
||||||
dependsConfig.forEach(entry -> depends.add(new ModDependencies.Entry(entry.get("id"), entry.get("versions"), entry.get("link"), entry.get("name"))));
|
List<UnmodifiableConfig> dependenciesConfig = config.get("frog.dependencies." + key);
|
||||||
|
if (dependenciesConfig != null) {
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
for (UnmodifiableConfig entry : dependenciesConfig) {
|
||||||
|
String entryId = entry.get("id");
|
||||||
|
String entryVersions = entry.get("versions");
|
||||||
|
|
||||||
|
if (entryId == null || entryId.isEmpty())
|
||||||
|
badProperties.add("frog.dependencies." + key + '.' + i + ".id");
|
||||||
|
|
||||||
|
result.add(new ModDependencies.Entry(entryId, entryVersions, entry.get("link"), entry.get("name")));
|
||||||
|
|
||||||
|
++i;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Collection<ModDependencies.Entry> breaks = new HashSet<>();
|
return result;
|
||||||
List<UnmodifiableConfig> breaksConfig = config.get("frog.dependencies.breaks");
|
};
|
||||||
if (breaksConfig != null) {
|
|
||||||
breaksConfig.forEach(entry -> breaks.add(new ModDependencies.Entry(entry.get("id"), entry.get("versions"), entry.get("link"), entry.get("name"))));
|
|
||||||
}
|
|
||||||
|
|
||||||
Collection<ModDependencies.Entry> suggests = new HashSet<>();
|
|
||||||
List<UnmodifiableConfig> suggestsConfig = config.get("frog.dependencies.suggests");
|
|
||||||
if (suggestsConfig != null) {
|
|
||||||
suggestsConfig.forEach(entry -> suggests.add(new ModDependencies.Entry(entry.get("id"), entry.get("versions"), entry.get("link"), entry.get("name"))));
|
|
||||||
}
|
|
||||||
|
|
||||||
Collection<ModDependencies.Entry> provides = new HashSet<>();
|
var depends = parseDependencies.apply("depends");
|
||||||
List<UnmodifiableConfig> providesConfig = config.get("frog.dependencies.provides");
|
var breaks = parseDependencies.apply("breaks");
|
||||||
if (providesConfig != null) {
|
var suggests = parseDependencies.apply("suggests");
|
||||||
providesConfig.forEach(entry -> provides.add(new ModDependencies.Entry(entry.get("id"), entry.get("version"), entry.get("link"), entry.get("name"))));
|
var provides = parseDependencies.apply("provides");
|
||||||
}
|
|
||||||
|
if (!badProperties.isEmpty())
|
||||||
|
throw new InvalidModPropertiesException(id, sources, badProperties);
|
||||||
|
|
||||||
UnmodifiableConfig extensionsConfig = config.get("frog.extensions");
|
UnmodifiableConfig extensionsConfig = config.get("frog.extensions");
|
||||||
Map<String, Object> extensions = new HashMap<>();
|
Map<String, Object> extensions = new HashMap<>();
|
||||||
if (extensionsConfig != null) {
|
if (extensionsConfig != null)
|
||||||
extensionsConfig.entrySet().forEach(entry -> extensions.put(entry.getKey(), entry.getValue()));
|
extensionsConfig.entrySet().forEach(entry -> extensions.put(entry.getKey(), entry.getValue()));
|
||||||
}
|
|
||||||
|
|
||||||
return new ModPropertiesImpl(id, name, icon, SemVerImpl.parse(version), license, Collections.unmodifiableMap(credits),
|
return new ModPropertiesImpl(id, name, icon, semVer, license, Collections.unmodifiableMap(credits),
|
||||||
new ModDependencies(depends, breaks, suggests, provides), ModExtensions.of(extensions), sources);
|
new ModDependencies(depends, breaks, suggests, provides), ModExtensions.of(extensions), sources);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -133,8 +175,26 @@ public class ModPropertiesReader {
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface Parser {
|
public interface Parser {
|
||||||
ModProperties parse(UnmodifiableConfig config, Collection<Path> sources) throws IOException;
|
ModProperties parse(UnmodifiableConfig config, Collection<Path> sources) throws IOException, InvalidModPropertiesException;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public static class InvalidModPropertiesException extends Exception {
|
||||||
|
|
||||||
|
private final Collection<String> invalid;
|
||||||
|
|
||||||
|
public InvalidModPropertiesException(String id, Collection<Path> sources, Collection<String> invalid) {
|
||||||
|
super(
|
||||||
|
"Invalid properties for %s (%s) - invalid or missing values for: %s".formatted(
|
||||||
|
Objects.requireNonNullElse(id, "<unknown>"),
|
||||||
|
sources.stream().map(Path::toString).collect(Collectors.joining(", ")),
|
||||||
|
String.join(", ", invalid)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
this.invalid = invalid;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue