Argument Parsing/Structure Advice

My Problem:

Based on the command structure I’m going for would it be viable to use the high-level command parsing, or would I need to manually parse them by making the commands implement CommandCallable? My knowledge is lacking when it comes to the wrapping of GenericArguments.

Command Structure:

  • /animate help
  • /animate create <name>
  • /animate delete <name> -f
  • /animate start <name> -f<num> -d<num> -c<num>
  • /animate stop <name>
  • /animate list
  • /animate <name> frame create <name> -h
  • /animate <name> frame duplicate <name|num> [num]
  • /animate <name> frame delete <name|num> -f
  • /animate <name> frame display <name|num>
  • /animate <name> frame update <name|num> -o
  • /animate <name> frame list

I was going to have help, create,…,stop and list be child commands of animate and give animate a string argument. And when the user specifies a name (which is just a string) I would then pass off the execution to the frame sub command which would have its child commands and their arguments. And that’s where I’m stuck, the first 6 commands are all fine and dandy until I try to incorporate the frame subcommand that is after a string argument.

My current CommandSpec is as such:

CommandSpec animate = CommandSpec.builder()                                                  
        .description(Text.of(Util.PRIMARY_COLOR, "Base animation command"))                  
        .child(createAnimation, "create")                                                    
        .child(deleteAnimation, "delete")                                                    
        .child(helpAnimation, "help", "?")                                                   
        .child(listAnimation, "list")                                                        
        .child(startAnimation, "start")                                                      
        .child(stopAnimation, "stop")                                                        
        .arguments(                                                                          
                GenericArguments.optional(GenericArguments.string(Text.of("animation_name")))
        )                                                                                    
        .permission(Permissions.ANIMATION_BASE)                                              
        .executor(new BaseAnimation())                                                       
        .build();                                                                            

In this senerio would it be necessary to use the low level command interface, or is it doable with the higher level CommandExector?

Much thanks!

You can always use GenericArguments.firstParsing(GenericArguments.seq()...) with the first in the sequence being a GenericArguments.literal.

So e.g. (static import for brevity)

literal(Text.of("frame"), "frame"),
firstParsing(
    seq(literal(Text.of("update"), "update"), new FrameElement(Text.of("frame"))),
    seq(literal(Text.of("duplicate"), "duplicate"), new FrameElement(Text.of("frame")), optional(integer(Text.of("num")))),
    // etc.
),

Assuming you have a FrameElement for the frame arg.

You could use a ChildCommandElementExecutor as your command element, but I will admit that I’m not completely sure on it’s usage, I just know it’s possible. With the upcoming command refactor, it’s something that’s already been asked for - I’ll try to ensure that it’s easier to add a child command part way through an argument string.

1 Like

Thank you @pie_flavor and @dualspiral for your very helpful suggestions.

For future viewers the final solution that I got working was the following:

CommandSpec animate = CommandSpec.builder()
                .description(Text.of(Util.PRIMARY_COLOR, "Base animation command"))
                .child(createAnimation, "create")
                .child(deleteAnimation, "delete")
                .child(helpAnimation, "help", "?")
                .child(listAnimation, "list")
                .child(startAnimation, "start")
                .child(stopAnimation, "stop")
                .arguments(
                        string(Text.of("animation_name")),
                        firstParsing(
                                // /animate <name> info
                                literal(Text.of("animation_info"), "info"),
                                // /animate <name> frame...
                                literal(Text.of("frame"), "frame")
                        ),
                        optional(
                        firstParsing(
                                // /animate <name> frame create <name> -h
                                seq(
                                        literal(Text.of("create"), "create"),
                                        string(Text.of("frame_name")),
                                        optional(flags().flag("h").buildWith(none()))
                                ),
                                // /animate <name> frame delete <name|num> -f
                                seq(
                                        literal(Text.of("delete"), "delete"),
                                        string(Text.of("frame_name_num")),
                                        optional(flags().flag("f").buildWith(none()))
                                ),
                                // /animate <name> frame display <name|num>
                                seq(
                                        literal(Text.of("display"), "display"),
                                        string(Text.of("frame_name_num")),
                                        optional(flags().flag("f").buildWith(none()))
                                ),
                                // /animate <name> frame duplicate <name|num> [num]
                                seq(
                                        literal(Text.of("duplicate"), "duplicate"),
                                        string(Text.of("frame_name_num")),
                                        optional(integer(Text.of("num")))
                                ),
                                // /animate <name> frame update <name|num> -o
                                seq(
                                        literal(Text.of("update"), "update"),
                                        string(Text.of("frame_name_num")),
                                        optional(flags().flag("o").buildWith(none()))
                                ),
                                // /animate <name> frame list
                                seq(
                                        literal(Text.of("list"), "list")
                                ),
                                // /animate <name> frame <name|num> info, this NEEDS to be last
                                seq(
                                        string(Text.of("frame_name_num")),
                                        literal(Text.of("frame_info"), "info")
                                )
                        )
                        )
                )
                .permission(Permissions.ANIMATION_BASE)
                .executor(new CommandGateKeeper())
                .build();

I wasn’t quite sure how to get the ChildCommandElementExecutor working, but pie’s suggestion worked wonders. The only thing I omitted for the time being was creating and implemented the FrameElement. There are a few things that I need to learn in regards to that, but it looks like a great thing to look into later.

@dualspiral I’ve been following your PR and am really excited for a new and improved command/argument system. Best of luck with that.

Thanks again.

@TheCahyag Thanks. If you have any wants for the command system, please add your thoughts to that issue!

1 Like