Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
a735c05
start setting up GrepMappingsCommand
supersaiyansubtlety Aug 17, 2025
0c43daf
add MemberTypeIndex
supersaiyansubtlety Aug 17, 2025
3b9118b
support array types in MemberTypeIndex
supersaiyansubtlety Aug 17, 2025
d79d979
support filtering void, primitive, and array types
supersaiyansubtlety Aug 17, 2025
ced7b88
fix GrepMappingsCommand::getName
supersaiyansubtlety Aug 17, 2025
6b65f2e
rethrow NumberFormatException for LIMIT arg
supersaiyansubtlety Aug 17, 2025
d99ece2
extract new Argument type descriptions
supersaiyansubtlety Aug 17, 2025
cd0a2f4
add GrepMappingsTest with first test implemented
supersaiyansubtlety Aug 18, 2025
c31f070
test finding methods
supersaiyansubtlety Aug 18, 2025
abb7eb3
reorder grep-mappings args
supersaiyansubtlety Aug 18, 2025
aadfaa2
format grepped type names like code
supersaiyansubtlety Aug 18, 2025
d7732e6
index params in EntryIndex
supersaiyansubtlety Aug 20, 2025
ca9ec7f
improve findsTypeFilteredParamNames and findsParamNames
supersaiyansubtlety Aug 20, 2025
9be2dfb
add findsEverythingAndLimitable to GrepMappingsTest
supersaiyansubtlety Aug 20, 2025
8df73d5
add findsNothing to GrepMappingsTest
supersaiyansubtlety Aug 20, 2025
e514e16
add PredicateParser with a few initial tests
supersaiyansubtlety Aug 21, 2025
6a3e47e
add more PredicateParserTests
supersaiyansubtlety Aug 21, 2025
a629786
checkstyle
supersaiyansubtlety Aug 21, 2025
236f322
fix throwing wrong exception on excess close parentheses
supersaiyansubtlety Aug 21, 2025
aa78b68
setup access predicate arguments
supersaiyansubtlety Aug 22, 2025
b5c049a
implement access filtering (untested)
supersaiyansubtlety Aug 22, 2025
4ce4e38
register grep command and indent its description
supersaiyansubtlety Aug 22, 2025
02320f9
improve limit arg explanation
supersaiyansubtlety Aug 22, 2025
55e9254
strengthen GrepMappingsTest assertions
supersaiyansubtlety Aug 22, 2025
03084b9
don't cache MethodEntry's params if method isn't indexed
supersaiyansubtlety Aug 22, 2025
1e4b754
complete GrepMappingsTest
supersaiyansubtlety Aug 22, 2025
7023bbd
javadoc PredicateParser
supersaiyansubtlety Aug 22, 2025
e1a1972
javadoc ArgsParser
supersaiyansubtlety Aug 22, 2025
1855071
rename grep-mappings -> search-mappings
supersaiyansubtlety Aug 23, 2025
d666cc1
prep for result sorting
supersaiyansubtlety Aug 23, 2025
824acca
implement SearchMappingsCommand sort
supersaiyansubtlety Aug 23, 2025
bdbc457
fix PARTS_ALPHABETIZER
supersaiyansubtlety Aug 23, 2025
772e9d4
improve formatting for unpackaged outer classes when sorting by name
supersaiyansubtlety Aug 23, 2025
0f07616
rename PACKAGE_DEPTH_SORTER -> PACKAGES_DEPTH_SORTER
supersaiyansubtlety Aug 24, 2025
f565331
make result order tests stricter
supersaiyansubtlety Aug 25, 2025
a49e3c2
minor test cleanup
supersaiyansubtlety Aug 31, 2025
dcb1f94
Merge branch 'develop/2.7' into grep-command
supersaiyansubtlety Sep 13, 2025
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
1 change: 1 addition & 0 deletions enigma-cli/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ plugins {

dependencies {
shadow(implementation project(':enigma'))
implementation libs.asm
testImplementation(testFixtures(project(':enigma')))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@
import java.util.function.BiFunction;
import java.util.stream.Stream;

/**
* Encapsulates logic for parsing a list of {@link Argument}s into values. Also provides access to the argument list.
*
* @param <P> the type the argument values are packed in
*/
final class ArgsParser<P> implements Iterable<Argument<?>> {
static <T> ArgsParser<T> of(Argument<T> arg) {
return new ArgsParser<>(ImmutableList.of(arg), (values, from) -> from.parse(arg, values));
Expand Down Expand Up @@ -119,6 +124,58 @@ static <T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, P> ArgsParser<P> of(
);
}

static <T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, P> ArgsParser<P> of(
Argument<T1> arg1, Argument<T2> arg2, Argument<T3> arg3, Argument<T4> arg4, Argument<T5> arg5,
Argument<T6> arg6, Argument<T7> arg7, Argument<T8> arg8, Argument<T9> arg9, Argument<T10> arg10,
Argument<T11> arg11,
Packer11<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, P> packer
) {
return new ArgsParser<>(
ImmutableList.of(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11),
(values, from) -> packer.pack(
from.parse(arg1, values), from.parse(arg2, values), from.parse(arg3, values),
from.parse(arg4, values), from.parse(arg5, values), from.parse(arg6, values),
from.parse(arg7, values), from.parse(arg8, values), from.parse(arg9, values),
from.parse(arg10, values), from.parse(arg11, values)
)
);
}

static <T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, P> ArgsParser<P> of(
Argument<T1> arg1, Argument<T2> arg2, Argument<T3> arg3, Argument<T4> arg4, Argument<T5> arg5,
Argument<T6> arg6, Argument<T7> arg7, Argument<T8> arg8, Argument<T9> arg9, Argument<T10> arg10,
Argument<T11> arg11, Argument<T12> arg12,
Packer12<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, P> packer
) {
return new ArgsParser<>(
ImmutableList.of(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12),
(values, from) -> packer.pack(
from.parse(arg1, values), from.parse(arg2, values), from.parse(arg3, values),
from.parse(arg4, values), from.parse(arg5, values), from.parse(arg6, values),
from.parse(arg7, values), from.parse(arg8, values), from.parse(arg9, values),
from.parse(arg10, values), from.parse(arg11, values), from.parse(arg12, values)
)
);
}

static <T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, P> ArgsParser<P> of(
Argument<T1> arg1, Argument<T2> arg2, Argument<T3> arg3, Argument<T4> arg4, Argument<T5> arg5,
Argument<T6> arg6, Argument<T7> arg7, Argument<T8> arg8, Argument<T9> arg9, Argument<T10> arg10,
Argument<T11> arg11, Argument<T12> arg12, Argument<T13> arg13,
Packer13<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, P> packer
) {
return new ArgsParser<>(
ImmutableList.of(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13),
(values, from) -> packer.pack(
from.parse(arg1, values), from.parse(arg2, values), from.parse(arg3, values),
from.parse(arg4, values), from.parse(arg5, values), from.parse(arg6, values),
from.parse(arg7, values), from.parse(arg8, values), from.parse(arg9, values),
from.parse(arg10, values), from.parse(arg11, values), from.parse(arg12, values),
from.parse(arg13, values)
)
);
}

private final ImmutableList<Argument<?>> args;

private final BiFunction<Map<String, String>, ArgParser, P> impl;
Expand Down Expand Up @@ -199,6 +256,21 @@ interface Packer10<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, P> {
P pack(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10);
}

@FunctionalInterface
interface Packer11<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, P> {
P pack(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11);
}

@FunctionalInterface
interface Packer12<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, P> {
P pack(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12);
}

@FunctionalInterface
interface Packer13<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, P> {
P pack(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13);
}

static final class Empty {
static final Empty INSTANCE = new Empty();

Expand Down
126 changes: 89 additions & 37 deletions enigma-cli/src/main/java/org/quiltmc/enigma/command/Argument.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import java.util.stream.Collectors;

/**
Expand All @@ -32,63 +34,81 @@ final class Argument<T> {
static final char NAME_DELIM = '=';

static final String ALTERNATIVES_DELIM = "|";

static final String BOOL_TYPE = true + ALTERNATIVES_DELIM + false;
static final String PATH_TYPE = "path";
static final String INT_TYPE = "int";
static final String PATTERN_TYPE = "regex";

static Argument<Path> ofPath(String name, String explanation) {
return new Argument<>(name, PATH_TYPE, string -> getPath(string).orElse(null), explanation);
return new Argument<>(name, PATH_TYPE, string -> parsePath(string).orElse(null), explanation);
}

static Argument<Path> ofFile(String name, String explanation) {
return new Argument<>(name, PATH_TYPE, Argument::getFile, explanation);
return new Argument<>(name, PATH_TYPE, Argument::parseFile, explanation);
}

static Argument<Path> ofFolder(String name, String explanation) {
return new Argument<>(name, PATH_TYPE, Argument::getFolder, explanation);
return new Argument<>(name, PATH_TYPE, Argument::parseFolder, explanation);
}

static Argument<Path> ofReadablePath(String name, String explanation) {
return new Argument<>(name, PATH_TYPE, Argument::getReadablePath, explanation);
return new Argument<>(name, PATH_TYPE, Argument::parseReadablePath, explanation);
}

static Argument<Path> ofReadableFile(String name, String explanation) {
return new Argument<>(name, PATH_TYPE, Argument::getReadableFile, explanation);
return new Argument<>(name, PATH_TYPE, Argument::parseReadableFile, explanation);
}

static Argument<Path> ofReadableFolder(String name, String explanation) {
return new Argument<>(name, PATH_TYPE, Argument::getReadableFolder, explanation);
return new Argument<>(name, PATH_TYPE, Argument::parseReadableFolder, explanation);
}

static Argument<Path> ofWritablePath(String name, String explanation) {
return new Argument<>(name, PATH_TYPE, Argument::getWritablePath, explanation);
return new Argument<>(name, PATH_TYPE, Argument::parseWritablePath, explanation);
}

static Argument<Path> ofWritableFile(String name, String explanation) {
return new Argument<>(name, PATH_TYPE, Argument::getWritableFile, explanation);
return new Argument<>(name, PATH_TYPE, Argument::parseWritableFile, explanation);
}

static Argument<Path> ofWritableFolder(String name, String explanation) {
return new Argument<>(name, PATH_TYPE, Argument::getWritableFolder, explanation);
return new Argument<>(name, PATH_TYPE, Argument::parseWritableFolder, explanation);
}

/**
* Creates a string argument whose {@code typeDescription} lists expected values.
*/
static Argument<String> ofLenientEnum(String name, Class<? extends Enum<?>> type, String explanation) {
final String alternatives = Arrays.stream(type.getEnumConstants())
.map(Object::toString)
.collect(Collectors.joining(ALTERNATIVES_DELIM));
return ofString(name, alternatives, explanation);
return ofString(name, alternativesOf(type), explanation);
}

static <E extends Enum<E>> Argument<E> ofEnum(String name, Class<E> type, String explanation) {
return new Argument<>(name, alternativesOf(type), string -> parseEnum(type, string), explanation);
}

static Argument<Boolean> ofBool(String name, String explanation) {
return new Argument<>(name, BOOL_TYPE, Boolean::parseBoolean, explanation);
return new Argument<>(name, BOOL_TYPE, Argument::parseBool, explanation);
}

static Argument<Integer> ofInt(String name, String explanation) {
return new Argument<>(name, INT_TYPE, Argument::parseInt, explanation);
}

static Argument<String> ofString(String name, String typeDescription, String explanation) {
return new Argument<>(name, typeDescription, Function.identity(), explanation);
}

static Argument<Pattern> ofPattern(String name, String explanation) {
return new Argument<>(name, PATTERN_TYPE, Argument::parsePattern, explanation);
}

private static String alternativesOf(Class<? extends Enum<?>> type) {
return Arrays.stream(type.getEnumConstants())
.map(Object::toString)
.collect(Collectors.joining(ALTERNATIVES_DELIM));
}

private final String name;
private final Function<String, T> fromString;
private final String displayForm;
Expand All @@ -111,48 +131,48 @@ static Argument<String> ofString(String name, String typeDescription, String exp
this.explanation = explanation;
}

static Path getFile(String path) {
return verifyFile(getPath(path)).orElse(null);
static Path parseFile(String path) {
return verifyFile(parsePath(path)).orElse(null);
}

static Path getFolder(String path) {
return verifyFolder(getPath(path)).orElse(null);
static Path parseFolder(String path) {
return verifyFolder(parsePath(path)).orElse(null);
}

static Path getReadablePath(String path) {
return getExistentPath(path).orElse(null);
static Path parseReadablePath(String path) {
return parseExistentPath(path).orElse(null);
}

static Path getReadableFile(String path) {
return verify(getExistentPath(path), Files::isRegularFile, "Not a file: ").orElse(null);
static Path parseReadableFile(String path) {
return verify(parseExistentPath(path), Files::isRegularFile, "Not a file: ").orElse(null);
}

static Path getReadableFolder(String path) {
return getExistentFolder(path).orElse(null);
static Path parseReadableFolder(String path) {
return parseExistentFolder(path).orElse(null);
}

static Path getWritablePath(String path) {
return getParentedPath(path).orElse(null);
static Path parseWritablePath(String path) {
return parseParentedPath(path).orElse(null);
}

static Path getWritableFile(String path) {
return verifyFile(getParentedPath(path)).orElse(null);
static Path parseWritableFile(String path) {
return verifyFile(parseParentedPath(path)).orElse(null);
}

static Path getWritableFolder(String path) {
return getExistentFolder(path).orElse(null);
static Path parseWritableFolder(String path) {
return parseExistentFolder(path).orElse(null);
}

static Optional<Path> getExistentFolder(String path) {
return verify(getExistentPath(path), Files::isDirectory, "Not a folder: ");
static Optional<Path> parseExistentFolder(String path) {
return verify(parseExistentPath(path), Files::isDirectory, "Not a folder: ");
}

static Optional<Path> getExistentPath(String path) {
return verify(getPath(path), Files::exists, "Cannot find path: ");
static Optional<Path> parseExistentPath(String path) {
return verify(parsePath(path), Files::exists, "Cannot find path: ");
}

static Optional<Path> getParentedPath(String path) {
return peek(getPath(path), child -> {
static Optional<Path> parseParentedPath(String path) {
return peek(parsePath(path), child -> {
final Path parent = child.getParent();
if (parent == null) {
throw new IllegalArgumentException("Cannot write path: " + child);
Expand All @@ -166,13 +186,45 @@ static Optional<Path> getParentedPath(String path) {
});
}

static Optional<Path> getPath(String path) {
static Optional<Path> parsePath(String path) {
return Optional.ofNullable(path)
.filter(string -> !string.isEmpty())
.map(Paths::get)
.map(Path::toAbsolutePath);
}

static Pattern parsePattern(String regex) {
if (regex == null || regex.isEmpty()) {
return null;
}

try {
return Pattern.compile(regex);
} catch (PatternSyntaxException e) {
throw new IllegalArgumentException(e);
}
}

static <E extends Enum<E>> E parseEnum(Class<E> type, String name) {
return name == null ? null : Enum.valueOf(type, name);
}

static Boolean parseBool(String value) {
return value == null ? null : Boolean.parseBoolean(value);
}

static Integer parseInt(String integer) {
if (integer == null || integer.isEmpty()) {
return null;
} else {
try {
return Integer.parseInt(integer);
} catch (NumberFormatException e) {
throw new IllegalArgumentException(e);
}
}
}

@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
static Optional<Path> verifyFile(Optional<Path> path) {
// !directory so it's true for non-existent files
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@
*/
public abstract sealed class Command<R, O> permits
CheckMappingsCommand, ComposeMappingsCommand, ConvertMappingsCommand, DecompileCommand, DeobfuscateCommand,
DropInvalidMappingsCommand, FillClassMappingsCommand, HelpCommand, InsertProposedMappingsCommand,
InvertMappingsCommand, MapSpecializedMethodsCommand, PrintStatsCommand {
DropInvalidMappingsCommand, FillClassMappingsCommand, SearchMappingsCommand, HelpCommand,
InsertProposedMappingsCommand, InvertMappingsCommand, MapSpecializedMethodsCommand, PrintStatsCommand {
private static final int EARLIEST_ARG_DELIM_INDEX = 1;

@VisibleForTesting
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ private FillClassMappingsCommand() {
void runImpl(Required required, Optional optional) throws Exception {
run(
required.inputJar, required.inputMappings, required.mappingOutput,
optional.fillAll, optional.obfuscatedNamespace, optional.deobfuscatedNamespace
optional.fillAll != null && optional.fillAll,
optional.obfuscatedNamespace, optional.deobfuscatedNamespace
);
}

Expand Down Expand Up @@ -133,5 +134,5 @@ private static void recursiveAddMappings(EntryTree<EntryMapping> mappings, JarIn

record Required(Path inputJar, Path inputMappings, Path mappingOutput) { }

record Optional(boolean fillAll, String obfuscatedNamespace, String deobfuscatedNamespace) { }
record Optional(Boolean fillAll, String obfuscatedNamespace, String deobfuscatedNamespace) { }
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ public class Main {
DropInvalidMappingsCommand.INSTANCE,
FillClassMappingsCommand.INSTANCE,
HelpCommand.INSTANCE,
PrintStatsCommand.INSTANCE
PrintStatsCommand.INSTANCE,
SearchMappingsCommand.INSTANCE
)
.collect(toImmutableMap(Command::getName, Function.identity()));

Expand Down
Loading