Coder Social home page Coder Social logo

kwcommands's Introduction

KWCommands

KWCommands is a complete rewrite of WCommands.

Welcome 1.0

@Cmd(description = "Welcome to KWCommands", requirements = 
      @Require(
            subject = @Id(User.class), 
            data = "repo.kwcommands.access", 
            infoType = Permission.class,
            testerType = PermissionTester.class)
)
public void welcome(@Info Logger logger, @Info User user) {
    logger.log(Level.INFO, "Welcome {0} to KWCommands", user.getName());
}

Welcome 1.0 (in 1.1)

@Cmd(description = "Welcome to KWCommands", requirements =
      @Require(
          subject = @Id(User.class),
          data = "repo.kwcommands.access",
          testerType = PermissionTester.class)
)
public void welcome(@Info Logger logger, @Info User user) {
    logger.log(Level.INFO, "Welcome {0} to KWCommands", user.getName());
}

Welcome 1.1

@CmdJson(type = CmdJsonType.RESOURCE, value = "/mycommands.json")
public class MyCommands {
    public void welcome(@Info Logger logger, @Info User user) {
        logger.log(Level.INFO, "Welcome {0} to KWCommands", user.getName());
    }
}

mycommands.json:

{
  "name": "mycommands",
  "description": "My commands",
  "subCommands": ["welcome.json"]
}

welcome.json:

{
  "name": "welcome",
  "description": "My commands",
  "handler": "method:welcome",
  "requiredInfo": [
    {
      "id": { "tags": ["logger"], "type": "Logger" }
    },
    {
      "id": { "tags": ["user"], "type": "User" }
    }
  ],
  "requirements": [
    {
      "info": { "tags": ["user"], "type": "User" },
      "tester": "com.my.PermissionTester",
      "data": "repo.kwcommands.access"
    }
  ]
}

kwcommands's People

Contributors

jonathanxd avatar

Watchers

 avatar  avatar  avatar

kwcommands's Issues

Support named arguments

Currently, argument does not have names (only id) and named argument is not supported, this is good for short commands, but sometimes commands are too complex, which makes it hard to understand and specify arguments, this purpose is to support named arguments, example: delete --directory mydir --recursive true, the way we specify the name is discutible:

  • Argument name prefixed with --, and in front of it (next element) is the argument value
    • Example: --recursive true
  • Argument name postfixed with : followed with argument value (same element)
    • Example: recursive:true
  • Mix of two: argument name prefix with -- and followed with = and argument value (same element)
    • Example: --recursive=true

Usage of escape character (\) before prefix and/or postfix, will cause entirely string to be considered an argument, example: delete \--recursive false will invoke command delete with arguments: --recursive as directory name and false as recursive value.

Argument type refactor

This is a solution for Validator and Possibilities that I've planned a time ago, before argument Map and List parsing implementation. I haven't implemented it because I was too lazy to think how to implement it.

The purpose is to introduce a real ArgumentType, other than Reflection one. ArgumentType descibres a data type that can be input for argument, a simple example is StringArgumentType, which only accepts string, and a complex is MapArgumentType, which can receive PairArgumentType, to describe pairs of values that is valid for MapArgumentType, or ComplexPairArgumentType, which describes all valid keys and respective values. ArgumentTypes also provides possibilities of input.

Single, Map and List parsers will parse values based on ArgumentType, meaning that, if provided ArgumentType is a SingleArgumentType, it parses the value as SingleInput, if the value is MapArgumentType, it parses the value as a Map, and if is ListArgumentType, parses as a List, deferring part of validation to InputParser instead of CommandParser.

This will viabilze the implementation of #11.

Object requirements in @Require

For obscure reasons, @Require only supports String requirements. The purpose is to add a dataProvider parameter that gets the value to check requirement

Separate process from dispatch

Yes, this adds a new class, but is more logic than having dispatch logic and parse logic in same class. Processor will still exist as wrapper of CommandParser and CommandDispatcher.

Support dynamic arguments

Currently, arguments are static, meaning that argument does not change based on other values provided to command, this also add support for List arguments without the needed of comma.

List argument

The evaluation of argument will be simple, when a List<Element> argument is found, consecutive inputs will be parsed until the Validator of Element returns false, example:

example 1 2 hello 3

fun example(values: List<Int>, str: String) {
  //...
}

The value of values will be a list of 1 and 2, the value of str will be hello. But the string will fail to be parsed, because there is no command registered with name 3.

Dynamic argument

An argument where, Validator, Transformer and PossibilitiesFunc receives parsed arguments, allowing dynamic validation, transformation and possibilities, so you will be able to create an argument whose input values are only the multiple of the last parsed value:

fun example(values: List<Int>) {
  ...
}

Input example 3 6 12 48 is valid, because 6 is multiple of 3, 12 is multiple of 6, 48 is multiple of 12, but example 3 6 12 48 56 is not a valid input, because 56 is not a multiple of 48.

Update 1

Map argument

Works in the same way as list arguments, but with = as the association keyword. Example:

create name="Example name" age=154

Argument Requirement

Currently we only support Information Requirement, the purpose is to support argument requirements.

Better information & id

Currently, we use an information identification with which requires two types of information, an array of string, which we call of tag, and a class, which we call the identification.

The problem is that we need more than these two values when fetching information, the other value we need is the type of information value. So is better to unify the identification with the type of information value, then, refering to a information using @Id(value = Player.class, tags = {"player"}) is the same as refering to a information of type Player.

The next problem is related to information system and inheritance, if we have following structure:

interface Permissible
interface Player : Permissible

And provide an information with Player type and try to fetch the Permissible one, we will have a big problem, because that information system does not support inheritance, some things will need a workaround that not worth it.

A thing we can do is change the way information system works and even the way we register and treat them. One of the goals of KWCommands was to reproduce every aspect of WCommands only with changes in the biggest problems of the project, but now, we need to change the small ones.

Information

Registered by tags: Array<String>, type: TypeInfo<out T> and value: T.

Tags

Identification alternatives, to ensure correctness or genericity

This is useful for fetching, you can register an information with following tags: sender, player and retrieve it by sender, player, or both to ensure that another information registered with killer and player, for example, will not be retrieved.

Absence of tags in registration, means that the information can only be retrieved without tags, abscence of them in retrieval, means that any information, regardless having tags or not (the without ones is priorized), should be fetched.

Manager?

Instead of having an InformationManager, which does not make sense, we will have an InformationList, the functionality is the same, but the naming changes to reflect real use.

Information value type

Used in both registration and retrieval, but when retrieving, the system will always use assignability instead of comparing by type, so if you register a List<Player>, you can retrieve it as List<? extends Permissible> (or List<Permissible>, but adding elements to list may be dangerous when you don't know the exact type of it).

Identification

We will use tags and TypeInfo. For annotations, we use both, class and string, but only one can be set to be valid.

Localizable command names

Currently the only way to have multi-language command name is using alises. The purpose is to have a way to define both Localizable command names and localizable aliases using JwIUtils Text utility.

Also we should introduce a "lang" entry on JsonCommandParser. The lang entry will receive a text base path (PATH) and then resolve the following elements based on path:

  • Command name (PATH.name)
  • Command aliases (PATH.alises)
  • Command description (PATH.description).

The same applies to arguments.

Specification provider in Annotations

The intention of this is to make the code more readable in case of larger specifications, example:

Without specification providers:

@Cmd(order = 100, description = "Starts the server", alias = arrayOf("start", "open"), requirements = arrayOf(
  Require(data = "canOpen", subject = Id(Environment::class, "environment"), infoType = Environment::class, testerType = PropertyChecker::class)
))
fun run(@Info(Id(Environment::class, "environment")) environment: Environment) {
}

With specification providers:

@Spec(CmdProvider::class)
fun run(@Spec(InfoProvider::class) environment: Environment) {
}

object CmdProvider : SpecProvider {
  override fun provide(): Spec { 
    return CommandSpec(
        order = 100, 
        description = "Starts the server", 
        alias = arrayOf("start", "open"),
        requirements = arrayOf(
          RequireSpec(data = "canOpen",
                      subject = IdSpec(Environment::class, "environment"),
                      infoType = Environment::class,
                      testerType = PropertyChecker::class)
        )
    )
  }
}

object InfoProvider : SpecProvider {
  override fun provide(): Spec { 
    return InfoSpec(id = IdSpec(Environment::class, "environment"))
  }
}

Or using existing classes (less classes to create, but I'm planning something that does not fit in this design).

@Spec(CmdProvider::class)
fun run(@Spec(InfoProvider::class) environment: Environment) {
}

object CmdProvider : CommandSpecProvider {
  override fun provide(enviroment: ReflectionEnviroment, queue: CommandFactoryQueue) { // This is required for sub-command dependencies...
    queue.add { Command(...) }
  }
}

object InfoProvider : SpecProvider {
  override fun provide(): Spec { 
    return Information(...)
  }
}

Auto complete

Introduce auto-complete API and partial command parsing (to allow auto-complete determine arguments).

Single Input support

Easy part, single input are simple, example:

  • play <TAB>
  • Suggestion: guitar

Map input support

Map input is not easy, it will require partial map parsing, examples:

  • Input: play <TAB>

  • Suggestion: {

  • Input: play {<TAB>

  • Suggestion: instrument

  • Input: player { instrument=<TAB>

  • Suggestion: guitar

  • Input: player { instrument=guitar<TAB>

  • Suggestion: , or }

List input support

Like map input support, list input support requires partial list parsing, but list is more simple, example:

  • Input player [guitar<TAB>

  • Suggestion: , or ]

  • Input player [guitar, <TAB>

  • Suggestion: violin

Partial parsing

Partial parsing will be introduced, it will be a major change, the first, in new parser. Parse functions will return ParseState instances, which contains parsed value, and if the input was parsed completelly or partially.

Suggestion

Suggestion will depend on values returned by PossibilitiesFunc.

Custom suggestions

Custom suggestion providers can be also registered, they will receive ParseState and return a List<String> with suggestions.

Validator and Possibilities change

Currently, both Validator and PossibilitiesFunc returns, Boolean and List<String> (with exception of 1.2 development line), which does not go well with purpose of dynamic argument validation, list and map arguments. Example:

register [name=KWCommands, url="https//github.com/JonathanxD/KWCommands"]

If the validator returns false for the map, we don't know exactly what is wrong, in this case is url (missing : after https).

For Validator

The purpose is to have an Validation that holds valid and invalid elements.

For single input arguments, we can simply use Validation.TRUE or Validation.FALSE without creating new instances of Validation, for list arguments, Validation receives indexes to point which elements are invalid. For map, Validation receives key, value or both. With only key, means that the key is not valid; with value only, means that value is not valid, regardless the key you associate it; with both (key and value), means that value is not valid for the key, but this does not necessary means that it is valid for other key.

For Possibilities

Possibilities class, for single elements, only a List with valid inputs, for list elements, a Map<Int, List<String>>. with elements valid for each list entry position (-1 to include all entry positions), for maps, a Map<String, List<String>> which contains possible values for every key (empty key means possible for all keys).

Post dispatch handler

Way to specify a handler to be called after dispatch of all commands, with and without reflection

Annotation processor

Uses annotation processing to generate a direct delegation of commands to methods, fields and classes.

Simple example:

class Server {
  @Command(description = "Starts the server")
  fun run(@Info(Id(Environment::class)) environment: Information<Environment>): Int {
    ...
  }
}

Generates something like this:

public final class ServerRunHandler implements Handler {
  private final Server server;
  
  public ServerRunHandler(Server server) { ... }

  public Object handle(CommandContainer container, InformationManager manager) {
    return this.server.run(manager.find(new Information.Id(Environment.class, new String[0]), TypeInfo.of(Environment.class)));
  }
}

Will add dependencies on:

Obs: This is incompatible with #1

Parser rewrite

Purpose

Write a simple and easily to maintain command parser. The parser will have following functions:

  • parse
    • Parses string list
  • parseCommand
    • Parses a command
  • parseArgument
    • Parses argument
  • parseSingleArgument
    • Parses argument with single value/single input
  • parseMultipleArgument
    • Parses multiple argument input
  • parseListArgument
    • Parses input list
  • parseMapArgument
    • Parses input argument

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.