Convert ModDependencies to an interface #15

Merged
owlsys merged 3 commits from TheKodeToad/mod-dependencies-iface into main 2024-09-02 12:09:57 -04:00
7 changed files with 110 additions and 124 deletions
Showing only changes of commit 0db877fd64 - Show all commits

View file

@ -1,43 +1,8 @@
package dev.frogmc.frogloader.api.mod; package dev.frogmc.frogloader.api.mod;
import java.util.*; import java.util.Collection;
import org.jetbrains.annotations.ApiStatus; public interface ModDependencies {
import org.jetbrains.annotations.Nullable;
/**
* A mod's dependencies.
*
* <p>Mod dependencies are declared in four types:</p>
* <code>Type.DEPEND</code>, <code>Type.BREAK</code>, <code>Type.SUGGEST</code> and <code>Type.PROVIDE</code>
*
* <p>Note: The <code>Type.PROVIDE</code> is a bit of a special case in the way that it only supports a single SemVer version instead of a full version range.</p>
*
* @see ModDependencies.Type
* @see ModDependencies.Entry
* @see ModProperties
* @see SemVer
*/
public final class ModDependencies {
private final Map<Type, Collection<Entry>> entries = new HashMap<>();
/**
* Construct new mod dependencies for a mod.
* <p><strong>Internal use only.</strong></p>
*
* @param depends <code>Type.DEPEND</code> entries
* @param breaks <code>Type.BREAK</code> entries
* @param suggests <code>Type.SUGGEST</code> entries
* @param provides <code>Type.PROVIDE</code> entries
*/
@ApiStatus.Internal
public ModDependencies(Collection<Entry> depends, Collection<Entry> breaks, Collection<Entry> suggests, Collection<Entry> provides) {
entries.put(Type.DEPEND, depends);
entries.put(Type.BREAK, breaks);
entries.put(Type.SUGGEST, suggests);
entries.put(Type.PROVIDE, provides);
}
/** /**
* Get dependencies of a specific mod for a specific type * Get dependencies of a specific mod for a specific type
@ -45,9 +10,7 @@ public final class ModDependencies {
* @param type the dependency type to query for * @param type the dependency type to query for
* @return a collection of dependency entries * @return a collection of dependency entries
*/ */
public Collection<Entry> getForType(Type type) { Collection<Entry> getForType(Type type);
return entries.get(type);
}
/** /**
* Get entries that depend on a specific mod's id * Get entries that depend on a specific mod's id
@ -55,17 +18,7 @@ public final class ModDependencies {
* @param id the mod id to find dependency entries for * @param id the mod id to find dependency entries for
* @return a collection of entries that depend on the given mod id * @return a collection of entries that depend on the given mod id
*/ */
public Collection<ModEntry> getForModId(String id) { Collection<Entry> getForModId(String id);
List<ModEntry> entries = new ArrayList<>();
for (Type type : Type.values()) {
for (Entry entry : getForType(type)) {
if (entry.id.equals(id)) {
entries.add(new ModEntry(type, entry.range, entry.link, entry.name));
}
}
}
return entries;
}
/** /**
* Dependency types to distinguish their variants. * Dependency types to distinguish their variants.
@ -89,70 +42,18 @@ public final class ModDependencies {
PROVIDE PROVIDE
} }
/** interface Entry {
* A data class to handle entries depending on a specific mod id
*/
public static class ModEntry {
private final Type type;
private final String range;
private final String link;
private final String name;
private ModEntry(Type type, String range, String link, String name) { Type type();
this.type = type;
this.range = range;
this.link = link;
this.name = name;
}
/** String id();
* Get this dependency's type
*
* @return This dependency's type
*/
public Type type() {
return type;
}
/** String range();
* Get this dependency's version range
*
* @return This dependency's version range
*/
public String range() {
return range;
}
/** String link();
* Get this dependency's link
* <p>May be null.</p>
*
* @return This dependency's link
*/
public @Nullable String link() {
return link;
}
/** String name();
* Get this dependency's (friendly) name
* <p>May be null.</p>
*
* @return This dependency's name
*/
public @Nullable String name() {
return name;
}
}
/**
* General storage for dependencies
*
* @param id the mod's id
* @param range the dependency's version range
* @param link an optional link for information about this dependency
* @param name an optional (friendly) name for this dependency
*/
public record Entry(String id, String range, String link, String name) {
} }
} }

View file

@ -6,11 +6,13 @@ import java.util.Map;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import dev.frogmc.frogloader.impl.mod.ModDependenciesImpl;
/** /**
* A mod's properties. This class represents a mod at runtime. * A mod's properties. This class represents a mod at runtime.
* It is read from each mod's <code>frog.mod.toml</code> file. * It is read from each mod's <code>frog.mod.toml</code> file.
* *
* @see ModDependencies * @see ModDependenciesImpl
* @see ModExtensions * @see ModExtensions
* @see SemVer * @see SemVer
*/ */
@ -65,7 +67,7 @@ public interface ModProperties {
* Get this mod's dependencies. * Get this mod's dependencies.
* *
* @return The mod's dependencies * @return The mod's dependencies
* @see ModDependencies * @see ModDependenciesImpl
*/ */
ModDependencies dependencies(); ModDependencies dependencies();

View file

@ -3,7 +3,6 @@ package dev.frogmc.frogloader.impl.mod;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
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.impl.SemVerParseException; import dev.frogmc.frogloader.impl.SemVerParseException;
@ -24,7 +23,7 @@ public class JavaModProperties {
SemVerImpl.parse(System.getProperty("java.runtime.version")), SemVerImpl.parse(System.getProperty("java.runtime.version")),
"", "",
Map.of(System.getProperty("java.vm.vendor"), Collections.singleton("Vendor")), Map.of(System.getProperty("java.vm.vendor"), Collections.singleton("Vendor")),
new ModDependencies(Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), Collections.emptySet()), new ModDependenciesImpl(Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), Collections.emptySet()),
new ModExtensionsImpl(Collections.emptyMap()), Collections.emptySet()); new ModExtensionsImpl(Collections.emptyMap()), Collections.emptySet());
} }
return INSTANCE; return INSTANCE;

View file

@ -0,0 +1,81 @@
package dev.frogmc.frogloader.impl.mod;
import java.util.*;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import dev.frogmc.frogloader.api.mod.ModDependencies;
import dev.frogmc.frogloader.api.mod.ModProperties;
import dev.frogmc.frogloader.api.mod.SemVer;
import dev.frogmc.frogloader.api.mod.ModDependencies.Entry;
import dev.frogmc.frogloader.api.mod.ModDependencies.Type;
/**
* A mod's dependencies.
*
* <p>Mod dependencies are declared in four types:</p>
* <code>Type.DEPEND</code>, <code>Type.BREAK</code>, <code>Type.SUGGEST</code> and <code>Type.PROVIDE</code>
*
* <p>Note: The <code>Type.PROVIDE</code> is a bit of a special case in the way that it only supports a single SemVer version instead of a full version range.</p>
*
* @see ModDependenciesImpl.Type
* @see ModDependenciesImpl.Entry
* @see ModProperties
* @see SemVer
*/
public final class ModDependenciesImpl implements ModDependencies {
private final Map<Type, Collection<ModDependencies.Entry>> entries = new HashMap<>();
/**
* Construct new mod dependencies for a mod.
* <p><strong>Internal use only.</strong></p>
*
* @param depends <code>Type.DEPEND</code> entries
* @param breaks <code>Type.BREAK</code> entries
* @param suggests <code>Type.SUGGEST</code> entries
* @param provides <code>Type.PROVIDE</code> entries
*/
@ApiStatus.Internal
kode marked this conversation as resolved
Review

the @ApiStatus.Internal annotation can be removed

the `@ApiStatus.Internal` annotation can be removed
public ModDependenciesImpl(Collection<ModDependencies.Entry> depends, Collection<ModDependencies.Entry> breaks, Collection<ModDependencies.Entry> suggests, Collection<ModDependencies.Entry> provides) {
entries.put(Type.DEPEND, depends);
entries.put(Type.BREAK, breaks);
entries.put(Type.SUGGEST, suggests);
entries.put(Type.PROVIDE, provides);
}
/**
* Get dependencies of a specific mod for a specific type
*
* @param type the dependency type to query for
* @return a collection of dependency entries
*/
@Override
public Collection<ModDependencies.Entry> getForType(Type type) {
return entries.get(type);
}
/**
* Get entries that depend on a specific mod's id
*
* @param id the mod id to find dependency entries for
* @return a collection of entries that depend on the given mod id
*/
@Override
public Collection<ModDependencies.Entry> getForModId(String id) {
List<ModDependencies.Entry> entries = new ArrayList<>();
for (Type type : Type.values()) {
for (ModDependencies.Entry entry : getForType(type)) {
if (entry.id().equals(id)) {
entries.add(entry);
}
}
}
return entries;
}
public record Entry(Type type, String id, String range, String link, String name) implements ModDependencies.Entry {
}
}

View file

@ -8,6 +8,7 @@ import java.util.stream.Collectors;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import com.google.common.collect.MultimapBuilder; import com.google.common.collect.MultimapBuilder;
import dev.frogmc.frogloader.api.mod.ModDependencies; import dev.frogmc.frogloader.api.mod.ModDependencies;
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.api.mod.SemVer;

View file

@ -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.BiFunction;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -14,6 +15,7 @@ import com.electronwill.nightconfig.core.CommentedConfig;
import com.electronwill.nightconfig.core.UnmodifiableConfig; import com.electronwill.nightconfig.core.UnmodifiableConfig;
import com.electronwill.nightconfig.core.file.FileNotFoundAction; import com.electronwill.nightconfig.core.file.FileNotFoundAction;
import com.electronwill.nightconfig.toml.TomlParser; 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;
@ -124,7 +126,7 @@ public class ModPropertiesReader {
} }
} }
Function<String, Collection<ModDependencies.Entry>> parseDependencies = key -> { BiFunction<ModDependencies.Type, String, Collection<ModDependencies.Entry>> parseDependencies = (type, key) -> {
Collection<ModDependencies.Entry> result = new HashSet<>(); Collection<ModDependencies.Entry> result = new HashSet<>();
List<UnmodifiableConfig> dependenciesConfig = config.get("frog.dependencies." + key); List<UnmodifiableConfig> dependenciesConfig = config.get("frog.dependencies." + key);
@ -138,7 +140,7 @@ public class ModPropertiesReader {
if (entryId == null || entryId.isEmpty()) if (entryId == null || entryId.isEmpty())
badProperties.add("frog.dependencies." + key + '.' + i + ".id"); badProperties.add("frog.dependencies." + key + '.' + i + ".id");
result.add(new ModDependencies.Entry(entryId, entryVersions, entry.get("link"), entry.get("name"))); result.add(new ModDependenciesImpl.Entry(type, entryId, entryVersions, entry.get("link"), entry.get("name")));
++i; ++i;
} }
@ -148,10 +150,10 @@ public class ModPropertiesReader {
}; };
var depends = parseDependencies.apply("depends"); var depends = parseDependencies.apply(ModDependencies.Type.DEPEND, "depends");
var breaks = parseDependencies.apply("breaks"); var breaks = parseDependencies.apply(ModDependencies.Type.BREAK, "breaks");
var suggests = parseDependencies.apply("suggests"); var suggests = parseDependencies.apply(ModDependencies.Type.SUGGEST, "suggests");
var provides = parseDependencies.apply("provides"); var provides = parseDependencies.apply(ModDependencies.Type.PROVIDE, "provides");
UnmodifiableConfig extensionsConfig = config.get("frog.extensions"); UnmodifiableConfig extensionsConfig = config.get("frog.extensions");
Map<ModExtensionsImpl.Key, Object> extensions = new HashMap<>(); Map<ModExtensionsImpl.Key, Object> extensions = new HashMap<>();
@ -169,7 +171,7 @@ public class ModPropertiesReader {
return new ModPropertiesImpl(id, name, icon, semVer, license, Collections.unmodifiableMap(credits), return new ModPropertiesImpl(id, name, icon, semVer, license, Collections.unmodifiableMap(credits),
new ModDependencies(depends, breaks, suggests, provides), new ModExtensionsImpl(extensions), sources); new ModDependenciesImpl(depends, breaks, suggests, provides), new ModExtensionsImpl(extensions), sources);
}); });
private static final Map<String, Parser> versions = Arrays.stream(values()).collect(Collectors.toMap(v -> v.version, v -> v.parser)); private static final Map<String, Parser> versions = Arrays.stream(values()).collect(Collectors.toMap(v -> v.version, v -> v.parser));

View file

@ -16,11 +16,11 @@ import java.util.stream.Collectors;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import dev.frogmc.frogloader.api.FrogLoader; import dev.frogmc.frogloader.api.FrogLoader;
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.plugin.GamePlugin; import dev.frogmc.frogloader.api.plugin.GamePlugin;
import dev.frogmc.frogloader.impl.FrogLoaderImpl; import dev.frogmc.frogloader.impl.FrogLoaderImpl;
import dev.frogmc.frogloader.impl.mod.ModDependenciesImpl;
import dev.frogmc.frogloader.impl.mod.ModExtensionsImpl; import dev.frogmc.frogloader.impl.mod.ModExtensionsImpl;
import dev.frogmc.frogloader.impl.mod.ModPropertiesImpl; import dev.frogmc.frogloader.impl.mod.ModPropertiesImpl;
import dev.frogmc.frogloader.impl.util.SystemProperties; import dev.frogmc.frogloader.impl.util.SystemProperties;
@ -204,7 +204,7 @@ public class MinecraftGamePlugin implements GamePlugin {
return new ModPropertiesImpl("minecraft", "Minecraft", "/assets/minecraft/textures/block/grass_block_side.png", return new ModPropertiesImpl("minecraft", "Minecraft", "/assets/minecraft/textures/block/grass_block_side.png",
MinecraftSemVerImpl.get(version), "MC-EULA", MinecraftSemVerImpl.get(version), "MC-EULA",
Map.of("Mojang AB", Collections.singleton("Author")), Map.of("Mojang AB", Collections.singleton("Author")),
new ModDependencies(Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), Collections.emptySet()), new ModDependenciesImpl(Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), Collections.emptySet()),
new ModExtensionsImpl(Collections.emptyMap()), Collections.emptySet()); new ModExtensionsImpl(Collections.emptyMap()), Collections.emptySet());
} }
} }