Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve command help #142

Merged
merged 5 commits into from
Aug 23, 2023
Merged
Show file tree
Hide file tree
Changes from 3 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
2 changes: 1 addition & 1 deletion enigma-cli/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ plugins {
}

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

Expand Down
88 changes: 88 additions & 0 deletions enigma-cli/src/main/java/cuchaz/enigma/command/Argument.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package cuchaz.enigma.command;

public enum Argument {
INPUT_JAR("<input-jar>",
"""
A path to the .jar file to use in executing the command."""
),
INPUT_MAPPINGS("<input-mappings>",
"""
A path to the file or folder to read mappings from."""
),
LEFT_MAPPINGS("<left-mappings>",
"""
A path to the left file or folder to read mappings from, used in commands which take two mapping inputs."""
),
RIGHT_MAPPINGS("<right-mappings>",
"""
A path to the right file or folder to read mappings from, used in commands which take two mapping inputs."""
),
OUTPUT_MAPPING_FORMAT("<output-mapping-format>",
"""
The mapping format to use when writing output mappings. Allowed values are (case-insensitive):
- TINY_V2:from_namespace:to_namespace (ex: tiny_v2:intermediary:named)
- ENIGMA_FILE
- ENIGMA_DIRECTORY
- ENIGMA_ZIP
- SRG_FILE
- RECAF

Proguard is not a valid output format, as writing is unsupported."""
),
MAPPING_OUTPUT("<mapping-output>",
"""
A path to the file or folder to write mappings to. Will be created if missing."""
),
KEEP_MODE("<keep-mode>",
"""
Which mappings that should be kept when composing mappings. Allowed values are "left", "right", and "both"."""
),
DECOMPILER("<decompiler>",
"""
The decompiler to use when producing output. Allowed values are (case-insensitive):
- VINEFLOWER
- CFR
- PROCYON
- BYTECODE"""
),
OUTPUT_FOLDER("<output-folder>",
"""
A path to the file or folder to write output to."""
),
OUTPUT_JAR("<output-jar>",
"""
A path to the .jar file to write output to. Will be created if missing."""
),
FILL_ALL("<fill-all>",
"""
Whether to fill all possible mappings. Allowed values are "true" and "false"."""
),
ENIGMA_PROFILE("<enigma-profile>",
"""
A path to an Enigma profile JSON file, used to apply things like plugins."""
);

private final String displayForm;
private final String explanation;

Argument(String displayForm, String explanation) {
this.displayForm = displayForm;
this.explanation = explanation;
}

public String getDisplayForm() {
return this.displayForm;
}

public String getExplanation() {
return this.explanation;
}

public ComposedArgument required() {
return new ComposedArgument(this, false);
}

public ComposedArgument optional() {
return new ComposedArgument(this, true);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,25 @@

public class CheckMappingsCommand extends Command {
public CheckMappingsCommand() {
super("checkmappings");
super(Argument.INPUT_JAR.required(),
Argument.INPUT_MAPPINGS.required());
}

@Override
public String getUsage() {
return "<in jar> <mappings file>";
public void run(String... args) throws Exception {
Path fileJarIn = getReadableFile(this.getArg(args, 0)).toPath();
Path fileMappings = getReadablePath(this.getArg(args, 1));
run(fileJarIn, fileMappings);
}

@Override
public boolean isValidArgument(int length) {
return length == 2;
public String getName() {
return "check-mappings";
}

@Override
public void run(String... args) throws Exception {
Path fileJarIn = getReadableFile(getArg(args, 0, "in jar", true)).toPath();
Path fileMappings = getReadablePath(getArg(args, 1, "mappings file", true));
run(fileJarIn, fileMappings);
public String getDescription() {
return "Validates the mappings for umm... something? idk really this always produces an error when running the tests but like it doesn't fail them? what's going on here??";
ix0rai marked this conversation as resolved.
Show resolved Hide resolved
}

public static void run(Path fileJarIn, Path fileMappings) throws Exception {
Expand Down
98 changes: 80 additions & 18 deletions enigma-cli/src/main/java/cuchaz/enigma/command/Command.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,83 @@
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import javax.annotation.Nullable;

public abstract class Command {
public final String name;
protected final List<Argument> requiredArguments = new ArrayList<>();
protected final List<Argument> optionalArguments = new ArrayList<>();
protected final List<ComposedArgument> allArguments;

protected Command(String name) {
this.name = name;
protected Command(ComposedArgument... arguments) {
this.allArguments = new ArrayList<>();

for (ComposedArgument argument : arguments) {
if (argument.optional()) {
this.optionalArguments.add(argument.argument());
this.allArguments.add(argument);
} else {
this.requiredArguments.add(argument.argument());
this.allArguments.add(argument);

if (!this.optionalArguments.isEmpty()) {
throw new IllegalArgumentException("optional arguments should be grouped at the end of command arguments! (declaring arg " + argument + ")");
}
}
}
}

public String getUsage() {
StringBuilder arguments = new StringBuilder();
appendArguments(arguments, this.requiredArguments);

if (!this.optionalArguments.isEmpty()) {
arguments.append(" [");
appendArguments(arguments, this.optionalArguments);
arguments.append("]");
}

return arguments.toString();
}

public abstract String getUsage();
private static void appendArguments(StringBuilder builder, List<Argument> arguments) {
for (int i = 0; i < arguments.size(); i++) {
builder.append(arguments.get(i).getDisplayForm()).append(i == arguments.size() - 1 ? "" : " ");
ix0rai marked this conversation as resolved.
Show resolved Hide resolved
}
}

public abstract boolean isValidArgument(int length);
/**
* Ensures that the amount of arguments provided is valid to the command.
* @param length the amount of arguments passed in
* @return {@code true} if the argument count is valid, {@code false} otherwise
*/
public boolean checkArgumentCount(int length) {
// valid if length is equal to the amount of required arguments or between required argument count and total argument count
return length == this.requiredArguments.size() || length > this.requiredArguments.size() && length <= this.requiredArguments.size() + this.optionalArguments.size();
ix0rai marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Executes this command.
* @param args the command-line arguments, to be parsed with {@link #getArg(String[], int)}
* @throws Exception on any error
*/
public abstract void run(String... args) throws Exception;

/**
* Returns the name of this command. Should be all-lowercase, and separated by dashes for words.
* Examples: {@code decompile}, {@code compose-mappings}, {@code fill-class-mappings}
* @return the name of the command
*/
public abstract String getName();

/**
* Returns a one-sentence description of this command's function, used in {@link HelpCommand}.
* @return the description
*/
public abstract String getDescription();

public static JarIndex loadJar(Path jar) throws IOException {
Logger.info("Reading JAR...");
JarClassProvider classProvider = new JarClassProvider(jar);
Expand All @@ -53,8 +113,18 @@ public static Enigma createEnigma() {
return Enigma.create();
}

public static Enigma createEnigma(EnigmaProfile profile) {
return createEnigma(profile, null);
/**
* Parses and validates the argument at {@code index}. The argument can then be converted to something more useful via {@link #getReadablePath(String)}, {@link #getWritablePath(String)}, etc.
* @param args the command-line args, provided in {@link #run(String...)}
* @param index the index of the argument
* @return the argument, as a string
*/
protected String getArg(String[] args, int index) {
if (index >= args.length || index >= this.allArguments.size()) {
return getArg(args, index, this.allArguments.get(index));
} else {
throw new RuntimeException("arg index is outside of range of possible arguments! (index: " + index + ", allowed arg count: " + this.allArguments.size() + ")");
}
}

public static Enigma createEnigma(EnigmaProfile profile, @Nullable Iterable<EnigmaPlugin> plugins) {
Expand All @@ -71,14 +141,6 @@ protected static EnigmaProject openProject(Path fileJarIn, Path fileMappings) th
return openProject(fileJarIn, fileMappings, createEnigma());
}

protected static EnigmaProject openProject(Path fileJarIn, Path fileMappings, EnigmaProfile profile) throws Exception {
return openProject(fileJarIn, fileMappings, profile, null);
}

protected static EnigmaProject openProject(Path fileJarIn, Path fileMappings, EnigmaProfile profile, @Nullable Iterable<EnigmaPlugin> plugins) throws Exception {
return openProject(fileJarIn, fileMappings, createEnigma(profile, plugins));
}

public static EnigmaProject openProject(Path fileJarIn, Path fileMappings, Enigma enigma) throws Exception {
ProgressListener progress = new ConsoleProgressListener();

Expand Down Expand Up @@ -177,10 +239,10 @@ protected static Path getWritablePath(String path) {
return dir;
}

protected static String getArg(String[] args, int i, String name, boolean required) {
private static String getArg(String[] args, int i, ComposedArgument argument) {
if (i >= args.length) {
if (required) {
throw new IllegalArgumentException(name + " is required");
if (!argument.optional()) {
throw new IllegalArgumentException(argument.argument().getDisplayForm() + " is required");
} else {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,33 @@

public class ComposeMappingsCommand extends Command {
public ComposeMappingsCommand() {
super("compose-mappings");
super(Argument.LEFT_MAPPINGS.required(),
Argument.RIGHT_MAPPINGS.required(),
Argument.OUTPUT_MAPPING_FORMAT.required(),
Argument.MAPPING_OUTPUT.required(),
Argument.KEEP_MODE.required());
}

@Override
public String getUsage() {
return "<left> <right> <result-format> <result> <keep-mode>";
public void run(String... args) throws IOException, MappingParseException {
Path left = getReadablePath(this.getArg(args, 0));
Path right = getReadablePath(this.getArg(args, 1));
String resultFormat = this.getArg(args, 2);
Path result = getWritablePath(this.getArg(args, 3));
String keepMode = this.getArg(args, 4);

run(left, right, resultFormat, result, keepMode);
}

@Override
public boolean isValidArgument(int length) {
return length == 5;
public String getName() {
return "compose-mappings";
}

@Override
public void run(String... args) throws IOException, MappingParseException {
Path left = getReadablePath(getArg(args, 0, "left", true));
Path right = getReadablePath(getArg(args, 1, "right", true));
String resultFormat = getArg(args, 2, "result-format", true);
Path result = getWritablePath(getArg(args, 3, "result", true));
String keepMode = getArg(args, 4, "keep-mode", true);

run(left, right, resultFormat, result, keepMode);
public String getDescription() {
// todo
return null;
ix0rai marked this conversation as resolved.
Show resolved Hide resolved
}

public static void run(Path leftFile, Path rightFile, String resultFormat, Path resultFile, String keepMode) throws IOException, MappingParseException {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package cuchaz.enigma.command;

public record ComposedArgument(Argument argument, boolean optional) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,28 @@

public class ConvertMappingsCommand extends Command {
public ConvertMappingsCommand() {
super("convert-mappings");
super(Argument.INPUT_MAPPINGS.required(),
Argument.OUTPUT_MAPPING_FORMAT.required(),
Argument.MAPPING_OUTPUT.required());
}

@Override
public String getUsage() {
return "<source> <result-format> <result>";
public void run(String... args) throws IOException, MappingParseException {
Path source = getReadablePath(this.getArg(args, 0));
String resultFormat = this.getArg(args, 1);
Path result = getWritablePath(this.getArg(args, 2));

run(source, resultFormat, result);
}

@Override
public boolean isValidArgument(int length) {
return length == 3;
public String getName() {
return "convert-mappings";
}

@Override
public void run(String... args) throws IOException, MappingParseException {
Path source = getReadablePath(getArg(args, 0, "source", true));
String resultFormat = getArg(args, 1, "result-format", true);
Path result = getWritablePath(getArg(args, 2, "result", true));

run(source, resultFormat, result);
public String getDescription() {
return "Converts the provided mappings to a different format.";
}

public static void run(Path source, String resultFormat, Path output) throws MappingParseException, IOException {
Expand Down
Loading