Describe the issue
We can agree that usage strings are very cool, very easy to use, very clean and readable, compared to other systems implemented in other frameworks with are, for example, an array of objects that lately face the issue of union
type, something easy to write thanks to the usage string. However, as easy as it is, the usage string has one weakness that we can't address in the current way, for that, I have multiple proposals.
UsagePrompt
This is @MrJacz's proposal, an array typeof Array<string | (msg: KlasaMessage) => string
. This is the easiest way to address this issue as it's another property for CommandOptions
: usagePrompt
. It's usage would come to be something similar to:
{
usage: '<battletag:string> [bronze|silver|gold|platinum]',
usageDelim: ' ',
usagePrompt: [
'Give me a battletag formatted as username#0000',
'Give me a rank name, can be bronze, silver, gold or platinum, pick the one that is next to your rank.'
]
}
However, it's lineal and to do so you may need to pass empty values or undefined in the middle of the array, to address that, it's possible to have a class do that:
{
usage: '<battletag:string> [bronze|silver|gold|platinum]',
usageDelim: ' ',
usagePrompt: new UsagePrompt()
.addPrompt(1,
'Give me a rank name, can be bronze, silver, gold or platinum, pick the one that is next to your rank.')
}
Where 0
, being <battletag:string>
, would use the default message (battletag is a required argument
).
However, it may be confusing, may it start with zero? Or maybe with one? To address this issue, we could make CommandOptions.usage
accept more types: not just string
.
ArgumentUsage
{
usage: new ArgumentUsage()
.addParameter('battletag', /(\w{4,12})#(\d{4,5})/, true,
'Give me a valid BattleTag, should be formatted like someuser#0000')
.addParameter('rank', 'bronze|silver|gold|platinum', false,
'Give me a rank name, can be bronze, silver, gold or platinum, pick the one that is next to your rank.'),
usageDelim: ' '
}
Where ArgumentUsage
is a class that compiles directly to ParsedUsage
and has a .toString()
method to compile to usageString
(used in the help command, for example). The reason this would compile to ParseUsage
is so it's able to add the new properties to the possibles: promptMessage
being either a string or callback (msg: KlasaMessage) => string
.
The Argument#addParameter
option is very powerful as it doesn't only add one more feature to Usage, which is prompt, but it also adds two more (nice) features: the possibility to have raw RegExp to use as regexp type (I'll be honest, I haven't figured the RegExp type since it got released, it's very complicated inside usageString, if this gets merged, the usage of the RegExp
type in Klasa will be significantly easier) or even a custom type. Of course, this part of the concept can change, and name would be... perhaps custom
or allow a fifth parameter which is parameter name for usageString. This can be reviewed later.
Furthermore, stringifying a RegExp for usageString is relatively easy, RegExp#toString()
does that.
This method would also support strings as legacy and would allow union type. Of course, this could use the current usageString parser, covering the downsides of the systems that are not based in quotedString.
The design of this method is inspired on PermissionLevels to keep the design of the framework and its core pieces.
EDIT: Stringifying an ArgumentUsage
instance would be as easy as mapping all the entries and write argName:typeName/typeParam
then wrap with <
and >
or [
and ]
depending on whether the value of the second parameter is true or false.
class ArgumentUsage {
public constructor();
private _entries: object[];
public addParameter(
argName: string,
type: string | RegExp | (msg: KlasaMessage, arg: string) => string,
required: boolean,
promptMessage?: string | (msg: KlasaMessage) => string,
typeName?: string): this;
public toJSON(): object;
public toString(): string;
}
EDIT 2: ArgumentUsage
is not meant to replace the former usageString
, but to complement as a more advanced tool that opens the gates for more creativity, implementing better debugging (IntelliSense, separation of the "parts" of each Tag, custom types, better RegExp readability...) and the possibilities to reach where usageString
can't reach while also using the parser for the aforementioned to cover the issues this kind of system has in other frameworks (lack of union type as an example).
This is more than just an enhanced usage, this allows better debugging, better internationalization, custom messages/prompts (feature highly requested over almost half a year) and improves user friendly-ness towards users.
Implementation
None.
Further details