Skip to content

Commit

Permalink
Merge pull request #21 from darvil82/dev
Browse files Browse the repository at this point in the history
Version 0.3.0
  • Loading branch information
darvil82 authored Jan 3, 2024
2 parents 4808ffc + 5ff4b9f commit 8b42546
Show file tree
Hide file tree
Showing 20 changed files with 360 additions and 199 deletions.
30 changes: 27 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@
public int age = 18;
@InitDef
public static void beforeInit(@NotNull CommandBuildHelper cmdBuildHelper) {
public static void beforeInit(@NotNull CommandBuildHelper helper) {
// configure the argument "age" to have an argument type of
// number range and set the range to 1-100
cmdBuildHelper.<NumberRangeArgumentType<Integer>, Integer>getArgument("age")
helper.<NumberRangeArgumentType<Integer>, Integer>getArgument("age")
.withArgType(new NumberRangeArgumentType<>(1, 100))
.onOk(v -> System.out.println("The age is valid!"));
}
Expand Down Expand Up @@ -81,4 +81,28 @@ The package is currently available on Repsy and GitHub Packages.
```
> [!NOTE]
> The `+` symbol is a wildcard that will automatically use the latest version of the package.
> You can also specify a specific version (e.g. `0.1.0`).
> You can also specify a specific version (e.g. `0.1.0`).


## FAQ

* ### Your logo has plenty of imperfections, please fix it.

The logo couldn't be simpler. I made it in five minutes in Figma. I am a terrible designer.
Also nothing is perfect. Heck, we are talking about software here, where bugs are
the norm, so, I think it's fine.

Also that isn't a question.

* ### Why the name "Lanat"?

I had a tough time finding a name for this project. I wanted something short, easy to remember,
and that sounded good. It had to be related to the project in some way.
Sadly most names I came up with were already taken... Anyway, so Lanat is actually an acronym for
**L**iterally **A**ll **N**ames **A**re **T**aken.
Yeah, I'm good at naming things.

* #### How is "Lanat" related to the project?

It's not.
4 changes: 2 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ plugins {
}

group = "com.darvil"
version = "0.2.1"
version = "0.3.0"
description = "Command line argument parser"

dependencies {
implementation("com.darvil:utils:0.0.2")
implementation("com.darvil:utils:0.1.0")
implementation("com.darvil:terminal-text-formatter:1.0.0")

implementation("org.jetbrains:annotations:24.0.1")
Expand Down
16 changes: 7 additions & 9 deletions src/main/java/lanat/Argument.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
* <p>
* An Argument specifies a value that the user can introduce to the command. This value will be parsed by the specified
* {@link ArgumentType} each time the Argument is used. Once finished parsing, the value may be retrieved by using
* {@link ParsedArguments#get(String)} on the {@link ParsedArguments} object returned by
* {@link ParseResult#get(String)} on the {@link ParseResult} object returned by
* {@link ArgumentParser#parse(CLInput)}.
*
* <p>
Expand Down Expand Up @@ -276,8 +276,8 @@ public void setDefaultValue(@Nullable TInner value) {
* names.
* <br><br>
* <p>
* Single character names can be used in argument name lists (e.g. <code>-abc</code>), each alphabetic character
* being an argument name, that is, <code>-a -b -c</code>.
* Single character names can be used in argument name lists (e.g. {@code -abc}), each alphabetic character
* being an argument name, that is, {@code -a -b -c}.
* </p>
*
* @param names the names that should be added to this argument.
Expand Down Expand Up @@ -463,8 +463,8 @@ public void setRepresentationColor(@NotNull Color color) {
/**
* Checks if this argument matches the given name, including the prefix.
* <p>
* For example, if the prefix is <code>'-'</code> and the argument has the name <code>"help"</code>, this method
* will return {@code true} if the name is <code>"--help"</code>.
* For example, if the prefix is {@code '-'} and the argument has the name {@code "help"}, this method
* will return {@code true} if the name is {@code "--help"}.
* </p>
*
* @param name the name to check
Expand Down Expand Up @@ -651,7 +651,7 @@ private PrefixChar(char character) {
* <p>
* <strong>NOTE:<br></strong>
* The constant fields of this class should be used instead of this method. Other characters could break
* compatibility with shells using special characters as prefixes, such as the <code>|</code> or <code>;</code>
* compatibility with shells using special characters as prefixes, such as the {@code |} or {@code ;}
* characters.
* </p>
*
Expand Down Expand Up @@ -749,6 +749,4 @@ public void invokeCallbacks() {
if (this.onErrorCallback == null) return;
this.onErrorCallback.accept(this);
}
}


}
4 changes: 2 additions & 2 deletions src/main/java/lanat/ArgumentGroup.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@
* arguments that are in different groups. For example, given the following group tree:
* <pre>
* +-----------------------+
* | Group 1 (restricted) |
* | Group 1 (restricted) |
* |-----------------------|
* | Argument 1 |
* +-----------------------+
* |
* +---------------------------+
* | |
* +---------------+ +-------------------------+
* | Group 2 | | Group 3 (restricted) |
* | Group 2 | | Group 3 (restricted) |
* |---------------| |-------------------------|
* | Argument 2.1 | | Argument 3.1 |
* | Argument 2.2 | | Argument 3.2 |
Expand Down
70 changes: 44 additions & 26 deletions src/main/java/lanat/ArgumentParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,14 @@
* <h2>Argument Parser</h2>
* <p>
* Provides the ability to parse a command line input and later gather the values of the parsed arguments.
* @see Command
*/
public class ArgumentParser extends Command {
/** This is used to be able to tell if we should reset the state of all the commands before parsing */
private boolean isParsed = false;
private @Nullable String license;
private @Nullable String version;


/**
* Creates a new command with the given name and description.
* @param programName The name of the command. This is the name the user will use to indicate that they want to use this
Expand All @@ -52,8 +53,9 @@ public ArgumentParser(@NotNull String programName) {

/**
* Creates a new command based on the given {@link CommandTemplate}. This does not take Sub-Commands into account.
* If you want to add Sub-Commands, use {@link #from(Class)} instead.
* If you want to add Sub-Commands, use {@link ArgumentParser#from(Class)} instead.
* @param templateClass The class of the template to use.
* @see CommandTemplate
*/
public ArgumentParser(@NotNull Class<? extends CommandTemplate> templateClass) {
super(templateClass);
Expand All @@ -62,6 +64,16 @@ public ArgumentParser(@NotNull Class<? extends CommandTemplate> templateClass) {
/**
* Constructs a new {@link ArgumentParser} based on the given {@link CommandTemplate}, taking Sub-Commands into
* account.
* <p>
* This is basically a shortcut for the following code:
* <pre>{@code
* new ArgumentParser(clazz) {{
* this.addCommand(new Command(subCmdClazz)); // do this for all possible sub-commands
* }};
* }</pre>
* This method basically makes it easier to add Sub-Commands to the given {@link CommandTemplate}. It looks for
* {@link lanat.CommandTemplate.CommandAccessor} annotations in the given class and adds the corresponding
* sub-commands to the {@link Command} object. This is done recursively.
* @param templateClass The class of the {@link CommandTemplate} to use.
* @return A new {@link ArgumentParser} based on the given {@link CommandTemplate}.
* @see CommandTemplate
Expand All @@ -81,14 +93,12 @@ public static ArgumentParser from(@NotNull Class<? extends CommandTemplate> temp
* <p>
* This is basically a shortcut for the following code:
* <pre>{@code
* new ArgumentParser(clazz).parse(input).into(clazz);
* ArgumentParser.from(clazz).parse(input).into(clazz);
* }</pre>
* <h4>Example:</h4>
* This code:
* <pre>{@code
* MyTemplate parsed = new ArgumentParser(MyTemplate.class) {{
* addCommand(new Command(MyTemplate.SubTemplate.class));
* }}
* ArgumentParser.from(MyTemplate.class)
* .parse(input)
* .printErrors()
* .exitIfErrors()
Expand All @@ -100,14 +110,19 @@ public static ArgumentParser from(@NotNull Class<? extends CommandTemplate> temp
* MyTemplate parsed = ArgumentParser.parseFromInto(MyTemplate.class, input);
* }
* </pre>
*
* The example above uses the {@link #parseFromInto(Class, CLInput)} overload, which sets the default options for
* the {@link AfterParseOptions} object.
* <p>
* This method uses {@link #from(Class)}. See that method for more info.
* @param templateClass The class to use as a template.
* @param input The input to parse.
* @param options A consumer that can be used for configuring the parsing process.
* @param <T> The type of the template.
* @return The parsed template.
* @see #parseFromInto(Class, CLInput)
* @see CommandTemplate
* @see #from(Class)
* @see AfterParseOptions
*/
public static <T extends CommandTemplate> @NotNull T parseFromInto(
@NotNull Class<T> templateClass,
Expand All @@ -124,12 +139,14 @@ public static ArgumentParser from(@NotNull Class<? extends CommandTemplate> temp
/**
* Constructs a new {@link ArgumentParser} based on the given {@link CommandTemplate}, parses the given input, and
* populates the template with the parsed values.
*
* <p>
* See {@link #parseFromInto(Class, CLInput, Consumer)} for more info.
* @param templateClass The class to use as a template.
* @param input The input to parse.
* @param <T> The type of the template.
* @return The parsed template.
* @see CommandTemplate
* @see #parseFromInto(Class, CLInput, Consumer)
*/
public static <T extends CommandTemplate>
@NotNull T parseFromInto(@NotNull Class<T> templateClass, @NotNull CLInput input) {
Expand Down Expand Up @@ -218,11 +235,11 @@ private void parseTokens() {


@Override
@NotNull ParsedArgumentsRoot getParsedArguments() {
return new ParsedArgumentsRoot(
@NotNull ParseResultRoot getParseResult() {
return new ParseResultRoot(
this,
this.getParser().getParsedArgumentsHashMap(),
this.getCommands().stream().map(Command::getParsedArguments).toList(),
this.getParser().getParsedArgsMap(),
this.getCommands().stream().map(Command::getParseResult).toList(),
this.getForwardValue()
);
}
Expand Down Expand Up @@ -359,10 +376,10 @@ public AfterParseOptions exitIfNoInput() {
}

/**
* Returns a {@link ParsedArgumentsRoot} object that contains all the parsed arguments.
* Returns a {@link ParseResultRoot} object that contains all the parsed arguments.
*/
public @NotNull ParsedArgumentsRoot getParsedArguments() {
return ArgumentParser.this.getParsedArguments();
public @NotNull ParseResultRoot getResult() {
return ArgumentParser.this.getParseResult();
}

/**
Expand All @@ -375,25 +392,25 @@ public AfterParseOptions exitIfNoInput() {
* @see CommandTemplate
*/
public <T extends CommandTemplate> T into(@NotNull Class<T> clazz) {
return AfterParseOptions.into(clazz, this.getParsedArguments());
return AfterParseOptions.into(clazz, this.getResult());
}

/**
* {@link #into(Class)} helper method.
* @param templateClass The Command Template class to instantiate.
* @param parsedArgs The parsed arguments to set the fields of the Command Template class.
* @param parseResult The parsed arguments to set the fields of the Command Template class.
*/
private static <T extends CommandTemplate> T into(
@NotNull Class<T> templateClass,
@NotNull ParsedArguments parsedArgs
@NotNull ParseResult parseResult
)
{
final T instance = UtlReflection.instantiate(templateClass);

// set the values of the fields
Stream.of(templateClass.getFields())
.filter(f -> f.isAnnotationPresent(Argument.Define.class))
.forEach(field -> AfterParseOptions.into$setFieldValue(field, parsedArgs, instance));
.forEach(field -> AfterParseOptions.into$setFieldValue(field, parseResult, instance));

// now handle the sub-command field accessors (if any)
Stream.of(templateClass.getDeclaredClasses())
Expand All @@ -410,30 +427,31 @@ private static <T extends CommandTemplate> T into(
);
});

AfterParseOptions.into$handleCommandAccessor(instance, commandAccesorField, parsedArgs);
AfterParseOptions.into$handleCommandAccessor(instance, commandAccesorField, parseResult);
});

instance.afterInstantiation(parseResult);
return instance;
}

/**
* {@link #into(Class)} helper method. Sets the value of the given field based on the parsed arguments.
* @param field The field to set the value of.
* @param parsedArgs The parsed arguments to set the field value from.
* @param parseResult The parsed arguments to set the field value from.
* @param instance The instance of the current Command Template class.
* @param <T> The type of the Command Template class.
*/
private static <T extends CommandTemplate> void into$setFieldValue(
@NotNull Field field,
@NotNull ParsedArguments parsedArgs,
@NotNull ParseResult parseResult,
@NotNull T instance
) {
final var annotation = field.getAnnotation(Argument.Define.class);

// get the name of the argument from the annotation or field name
final String argName = annotation.names().length == 0 ? field.getName() : annotation.names()[0];

final @NotNull Optional<?> parsedValue = parsedArgs.get(argName);
final @NotNull Optional<?> parsedValue = parseResult.get(argName);

try {
// if the field has a value already set and the parsed value is empty, skip it (keep the old value)
Expand Down Expand Up @@ -470,13 +488,13 @@ private static <T extends CommandTemplate> T into(
* {@link #into(Class)} helper method. Handles the {@link CommandTemplate.CommandAccessor} annotation.
* @param parsedTemplateInstance The instance of the current Command Template class.
* @param commandAccesorField The field annotated with {@link CommandTemplate.CommandAccessor}.
* @param parsedArgs The parsed arguments to set the fields of the Command Template class.
* @param parseResult The parsed arguments to set the fields of the Command Template class.
*/
@SuppressWarnings("unchecked")
private static <T extends CommandTemplate> void into$handleCommandAccessor(
@NotNull T parsedTemplateInstance,
@NotNull Field commandAccesorField,
@NotNull ParsedArguments parsedArgs
@NotNull ParseResult parseResult
)
{
final Class<?> fieldType = commandAccesorField.getType();
Expand All @@ -493,7 +511,7 @@ private static <T extends CommandTemplate> T into(
commandAccesorField.set(parsedTemplateInstance,
AfterParseOptions.into(
(Class<T>)fieldType,
parsedArgs.getSubParsedArgs(cmdName)
parseResult.getSubResult(cmdName)
)
);
} catch (IllegalAccessException e) {
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/lanat/ArgumentType.java
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ int getLastReceivedValuesNum() {

/**
* Iterates over the values that this argument received when being parsed. This also sets
* <code>this.currentArgValueIndex</code> to the current index of the value.
* {@code this.currentArgValueIndex} to the current index of the value.
*
* @param args The values that this argument received when being parsed.
* @param consumer The consumer that will be called for each value.
Expand Down
Loading

0 comments on commit 8b42546

Please sign in to comment.