CommandResolver

A helper class where you can define command "sentences", and then resolve (either partially or fully) commands from "sentences" provided by the user.

Constructors

this
this()
Undocumented in source.

Members

Aliases

NodeT
alias NodeT = CommandNode!UserDataT

The CommandNode instatiation for this resolver.

Functions

define
void define(string commandSentence, UserDataT userDataForFinalNode)

Defines a command sentence.

resolve
CommandResolveResult!UserDataT resolve(RangeOfStrings words)
CommandResolveResult!UserDataT resolve(string sentence)

Attempts to resolve a range of words/a sentence into a CommandNode.

resolveAndAdvance
CommandResolveResult!UserDataT resolveAndAdvance(ArgPullParser parser)

Peforms the same task as CommandResolver.resolve, except that it will also advance the given parser to the next unparsed argument.

Properties

finalWords
NodeT[] finalWords [@property getter]

Notes: While the returned array is mutable, the nodes stored in this array are *not* the same nodes stored in the actual search tree. This means that any changes made to this array will not be reflected by the results of resolve and resolveAndAdvance.

root
NodeT root [@property getter]

Parameters

UserDataT

User-provided data for each command (CommandNodes of type CommandNodeType.finalWord).

Description: In essence, this class is just an abstraction around a basic tree structure (CommandNode), to make it easy to both define and search the tree.

First of all, JCLI supports commands having multiple "words" within them, such as "build all libs"; "remote get-url", etc. This entire collection of "words" is referred to as a "sentence".

The tree for the resolver consists of words pointing to any following words (CommandNodeType.partialWord), ultimately ending each branch with the final command word (CommandNodeType.finalWord).

For example, if we had the following commands "build libs"; "build apps", and "test libs", the tree would look like the following.

Examples

1 import std.algorithm : map, equal;
2 
3 // Define UserData as a struct containing an execution method. Define a UserData which toggles a value.
4 static struct UserData
5 {
6     void delegate() @safe execute;
7 }
8 
9 bool executeValue;
10 void toggleValue() @safe
11 {
12     executeValue = !executeValue;
13 }
14 
15 auto userData = UserData(&toggleValue);
16 
17 // Create the resolver and define three command paths: "toggle", "please toggle", and "please tog".
18 // Tree should look like:
19 //       [root]
20 //      /      \
21 // toggle       please
22 //             /      \
23 //          toggle    tog
24 auto resolver = new CommandResolver!UserData;
25 resolver.define("toggle", userData);
26 resolver.define("please toggle", userData);
27 resolver.define("please tog", userData);
28 
29 // Resolve 'toggle' and call its execute function.
30 auto result = resolver.resolve("toggle");
31 assert(result.success);
32 assert(result.value.word == "toggle");
33 assert(result.value.sentence == "toggle");
34 assert(result.value.type == CommandNodeType.finalWord);
35 assert(result.value.userData.execute !is null);
36 result.value.userData.execute();
37 assert(executeValue == true);
38 
39 // Resolve 'please' and confirm that it's only a partial match.
40 result = resolver.resolve("please");
41 assert(result.success);
42 assert(result.value.word            == "please");
43 assert(result.value.sentence        is null);
44 assert(result.value.type            == CommandNodeType.partialWord);
45 assert(result.value.children.length == 2);
46 assert(result.value.userData        == UserData.init);
47 
48 // Resolve 'please toggle' and call its execute function.
49 result = resolver.resolve("please toggle");
50 assert(result.success);
51 assert(result.value.word == "toggle");
52 assert(result.value.sentence == "please toggle");
53 assert(result.value.type == CommandNodeType.finalWord);
54 result.value.userData.execute();
55 assert(executeValue == false);
56 
57 // Resolve 'please tog' and call its execute function. (to test nodes with multiple children).
58 result = resolver.resolve("please tog");
59 assert(result.success);
60 assert(result.value.word == "tog");
61 assert(result.value.sentence == "please tog");
62 assert(result.value.type == CommandNodeType.finalWord);
63 result.value.userData.execute();
64 assert(executeValue == true);
65 
66 // Resolve a few non-existing command sentences, and ensure that they were unsuccessful.
67 assert(!resolver.resolve(null).success);
68 assert(!resolver.resolve("toggle please").success);
69 assert(!resolver.resolve("He she we, wombo.").success);
70 
71 // Test that final words are properly tracked.
72 assert(resolver.finalWords.map!(w => w.word).equal(["toggle", "toggle", "tog"]));
73 assert(resolver.root.finalWords.equal(resolver.finalWords));
74 
75 auto node = resolver.resolve("please").value;
76 assert(node.finalWords().map!(w => w.word).equal(["toggle", "tog"]));

Meta