summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRémi Nicole <nicole@kolabsystems.com>2018-08-03 16:59:16 +0200
committerChristian Mollekopf <chrigi_1@fastmail.fm>2018-08-03 17:10:31 +0200
commit50bed81038f10091d35c5719df8078612393ae95 (patch)
treee4a3b634118b2b1b9fd88203902e934299deac9b
parentad3dc221273100ba61828f993f1d1c8a1272d665 (diff)
downloadsink-50bed81038f10091d35c5719df8078612393ae95.tar.gz
sink-50bed81038f10091d35c5719df8078612393ae95.zip
Improve documentation of SinkSH
Summary: - Add support for positional arguments, options, flags in the syntax tree - Add automatic generation of usage string TODO: - ~~Better document the `--reduce` option in the `list` command~~ - Get the parent command for sub-commands so we could show help for `trace on` and not just `on` - Pass the syntax to the implementation of the command so commands can easily show help on wrong usage. Also, SyntaxTree could do automatic input validation in the future, this could also help. - Even with the new documentation, some command could still be confusing. Should we add some usage examples? Reviewers: cmollekopf Reviewed By: cmollekopf Tags: #sink Differential Revision: https://phabricator.kde.org/D14547
-rw-r--r--sinksh/syntax_modules/core_syntax.cpp14
-rw-r--r--sinksh/syntax_modules/sink_clear.cpp7
-rw-r--r--sinksh/syntax_modules/sink_count.cpp10
-rw-r--r--sinksh/syntax_modules/sink_create.cpp35
-rw-r--r--sinksh/syntax_modules/sink_drop.cpp6
-rw-r--r--sinksh/syntax_modules/sink_inspect.cpp22
-rw-r--r--sinksh/syntax_modules/sink_list.cpp18
-rw-r--r--sinksh/syntax_modules/sink_livequery.cpp15
-rw-r--r--sinksh/syntax_modules/sink_modify.cpp27
-rw-r--r--sinksh/syntax_modules/sink_remove.cpp28
-rw-r--r--sinksh/syntax_modules/sink_stat.cpp11
-rw-r--r--sinksh/syntax_modules/sink_sync.cpp5
-rw-r--r--sinksh/syntax_modules/sink_trace.cpp7
-rw-r--r--sinksh/syntaxtree.cpp98
-rw-r--r--sinksh/syntaxtree.h23
15 files changed, 262 insertions, 64 deletions
diff --git a/sinksh/syntax_modules/core_syntax.cpp b/sinksh/syntax_modules/core_syntax.cpp
index e90d894..07acc28 100644
--- a/sinksh/syntax_modules/core_syntax.cpp
+++ b/sinksh/syntax_modules/core_syntax.cpp
@@ -57,19 +57,7 @@ bool showHelp(const QStringList &commands, State &state)
57 if (!syntax->help.isEmpty()) { 57 if (!syntax->help.isEmpty()) {
58 state.print(": " + syntax->help); 58 state.print(": " + syntax->help);
59 } 59 }
60 state.printLine(); 60 state.printLine(QString("\n\n") + syntax->usage());
61
62 if (!syntax->children.isEmpty()) {
63 state.printLine("Sub-commands:", 1);
64 QSet<QString> sorted;
65 for (auto childSyntax: syntax->children) {
66 sorted.insert(childSyntax.keyword);
67 }
68
69 for (auto keyword: sorted) {
70 state.printLine(keyword, 1);
71 }
72 }
73 } else { 61 } else {
74 state.printError("Unknown command: " + commands.join(" ")); 62 state.printError("Unknown command: " + commands.join(" "));
75 } 63 }
diff --git a/sinksh/syntax_modules/sink_clear.cpp b/sinksh/syntax_modules/sink_clear.cpp
index e676dd6..ca38943 100644
--- a/sinksh/syntax_modules/sink_clear.cpp
+++ b/sinksh/syntax_modules/sink_clear.cpp
@@ -35,10 +35,12 @@
35namespace SinkClear 35namespace SinkClear
36{ 36{
37 37
38Syntax::List syntax();
39
38bool clear(const QStringList &args, State &state) 40bool clear(const QStringList &args, State &state)
39{ 41{
40 if (args.isEmpty()) { 42 if (args.isEmpty()) {
41 state.printError(QObject::tr("Please provide at least one resource to clear.")); 43 state.printError(syntax()[0].usage());
42 return false; 44 return false;
43 } 45 }
44 for (const auto &resource : args) { 46 for (const auto &resource : args) {
@@ -53,6 +55,9 @@ bool clear(const QStringList &args, State &state)
53Syntax::List syntax() 55Syntax::List syntax()
54{ 56{
55 Syntax clear("clear", QObject::tr("Clears the local cache of one or more resources (be careful!)"), &SinkClear::clear, Syntax::NotInteractive); 57 Syntax clear("clear", QObject::tr("Clears the local cache of one or more resources (be careful!)"), &SinkClear::clear, Syntax::NotInteractive);
58
59 clear.addPositionalArgument({.name = "resource", .help = "The resource to clear"});
60
56 clear.completer = &SinkshUtils::resourceCompleter; 61 clear.completer = &SinkshUtils::resourceCompleter;
57 62
58 return Syntax::List() << clear; 63 return Syntax::List() << clear;
diff --git a/sinksh/syntax_modules/sink_count.cpp b/sinksh/syntax_modules/sink_count.cpp
index 04a9550..1edf1c2 100644
--- a/sinksh/syntax_modules/sink_count.cpp
+++ b/sinksh/syntax_modules/sink_count.cpp
@@ -37,12 +37,14 @@
37namespace SinkCount 37namespace SinkCount
38{ 38{
39 39
40Syntax::List syntax();
41
40bool count(const QStringList &args, State &state) 42bool count(const QStringList &args, State &state)
41{ 43{
42 Sink::Query query; 44 Sink::Query query;
43 query.setId("count"); 45 query.setId("count");
44 if (!SinkshUtils::applyFilter(query, SyntaxTree::parseOptions(args))) { 46 if (!SinkshUtils::applyFilter(query, SyntaxTree::parseOptions(args))) {
45 state.printError(QObject::tr("Options: $type $filter")); 47 state.printError(syntax()[0].usage());
46 return false; 48 return false;
47 } 49 }
48 50
@@ -63,7 +65,11 @@ bool count(const QStringList &args, State &state)
63 65
64Syntax::List syntax() 66Syntax::List syntax()
65{ 67{
66 Syntax count("count", QObject::tr("Returns the number of items of a given type in a resource. Usage: count <type> <resource>"), &SinkCount::count, Syntax::EventDriven); 68 Syntax count("count", QObject::tr("Returns the number of items of a given type in a resource"), &SinkCount::count, Syntax::EventDriven);
69
70 count.addPositionalArgument({.name = "type", .help = "The entity type to count"});
71 count.addPositionalArgument({.name = "resource", .help = "A resource id where to count", .required = false});
72
67 count.completer = &SinkshUtils::typeCompleter; 73 count.completer = &SinkshUtils::typeCompleter;
68 74
69 return Syntax::List() << count; 75 return Syntax::List() << count;
diff --git a/sinksh/syntax_modules/sink_create.cpp b/sinksh/syntax_modules/sink_create.cpp
index f18a990..b1631cd 100644
--- a/sinksh/syntax_modules/sink_create.cpp
+++ b/sinksh/syntax_modules/sink_create.cpp
@@ -40,15 +40,12 @@ using namespace Sink;
40namespace SinkCreate 40namespace SinkCreate
41{ 41{
42 42
43Syntax::List syntax();
44
43bool create(const QStringList &allArgs, State &state) 45bool create(const QStringList &allArgs, State &state)
44{ 46{
45 if (allArgs.isEmpty()) {
46 state.printError(QObject::tr("A type is required"), "sinkcreate/02");
47 return false;
48 }
49
50 if (allArgs.count() < 2) { 47 if (allArgs.count() < 2) {
51 state.printError(QObject::tr("A resource ID is required to create items"), "sinkcreate/03"); 48 state.printError(syntax()[0].usage());
52 return false; 49 return false;
53 } 50 }
54 51
@@ -173,15 +170,33 @@ bool identity(const QStringList &args, State &state)
173 return true; 170 return true;
174} 171}
175 172
176
177Syntax::List syntax() 173Syntax::List syntax()
178{ 174{
179 Syntax::List syntax; 175 Syntax::List syntax;
180 176
181 Syntax create("create", QObject::tr("Create items in a resource"), &SinkCreate::create); 177 Syntax create("create", QObject::tr("Create items in a resource"), &SinkCreate::create);
182 create.children << Syntax("resource", QObject::tr("Creates a new resource"), &SinkCreate::resource); 178 create.addPositionalArgument({ .name = "type", .help = "The type of entity to create (mail, event, etc.)" });
183 create.children << Syntax("account", QObject::tr("Creates a new account"), &SinkCreate::account); 179 create.addPositionalArgument({ .name = "resourceId", .help = "The ID of the resource that will contain the new entity" });
184 create.children << Syntax("identity", QObject::tr("Creates a new identity"), &SinkCreate::identity); 180 create.addPositionalArgument(
181 { .name = "key value", .help = "Content of the entity", .required = false, .variadic = true });
182
183 Syntax resource("resource", QObject::tr("Creates a new resource"), &SinkCreate::resource);
184 resource.addPositionalArgument({ .name = "type", .help = "The type of resource to create" });
185 resource.addPositionalArgument(
186 { .name = "key value", .help = "Content of the resource", .required = false, .variadic = true });
187
188 Syntax account("account", QObject::tr("Creates a new account"), &SinkCreate::account);
189 account.addPositionalArgument({ .name = "type", .help = "The type of account to create" });
190 account.addPositionalArgument(
191 { .name = "key value", .help = "Content of the account", .required = false, .variadic = true });
192
193 Syntax identity("identity", QObject::tr("Creates a new identity"), &SinkCreate::identity);
194 identity.addPositionalArgument(
195 { .name = "key value", .help = "Content of the identity", .required = false, .variadic = true });
196
197 create.children << resource;
198 create.children << account;
199 create.children << identity;
185 200
186 syntax << create; 201 syntax << create;
187 return syntax; 202 return syntax;
diff --git a/sinksh/syntax_modules/sink_drop.cpp b/sinksh/syntax_modules/sink_drop.cpp
index 3b9a817..eaa3041 100644
--- a/sinksh/syntax_modules/sink_drop.cpp
+++ b/sinksh/syntax_modules/sink_drop.cpp
@@ -35,10 +35,12 @@
35namespace SinkDrop 35namespace SinkDrop
36{ 36{
37 37
38Syntax::List syntax();
39
38bool drop(const QStringList &args, State &state) 40bool drop(const QStringList &args, State &state)
39{ 41{
40 if (args.isEmpty()) { 42 if (args.isEmpty()) {
41 state.printError(QObject::tr("Please provide at least one resource to drop.")); 43 state.printError(syntax()[0].usage());
42 return false; 44 return false;
43 } 45 }
44 46
@@ -60,6 +62,8 @@ bool drop(const QStringList &args, State &state)
60Syntax::List syntax() 62Syntax::List syntax()
61{ 63{
62 Syntax drop("drop", QObject::tr("Drop all caches of a resource."), &SinkDrop::drop, Syntax::NotInteractive); 64 Syntax drop("drop", QObject::tr("Drop all caches of a resource."), &SinkDrop::drop, Syntax::NotInteractive);
65 drop.addPositionalArgument({.name = "resource", .help = "Id(s) of the resource(s) to drop", .required = true, .variadic = true});
66
63 drop.completer = &SinkshUtils::resourceOrTypeCompleter; 67 drop.completer = &SinkshUtils::resourceOrTypeCompleter;
64 return Syntax::List() << drop; 68 return Syntax::List() << drop;
65} 69}
diff --git a/sinksh/syntax_modules/sink_inspect.cpp b/sinksh/syntax_modules/sink_inspect.cpp
index f9cc50a..355efa3 100644
--- a/sinksh/syntax_modules/sink_inspect.cpp
+++ b/sinksh/syntax_modules/sink_inspect.cpp
@@ -55,10 +55,14 @@ QString parse(const QByteArray &bytes)
55 } 55 }
56} 56}
57 57
58Syntax::List syntax();
59
58bool inspect(const QStringList &args, State &state) 60bool inspect(const QStringList &args, State &state)
59{ 61{
60 if (args.isEmpty()) { 62 if (args.isEmpty()) {
61 state.printError(QObject::tr("Options: [--resource $resource] ([--db $db] [--filter $id] [--showinternal] | [--validaterids $type] | [--fulltext [$id]])")); 63 //state.printError(QObject::tr("Options: [--resource $resource] ([--db $db] [--filter $id] [--showinternal] | [--validaterids $type] | [--fulltext [$id]])"));
64 state.printError(syntax()[0].usage());
65 return false;
62 } 66 }
63 auto options = SyntaxTree::parseOptions(args); 67 auto options = SyntaxTree::parseOptions(args);
64 auto resource = SinkshUtils::parseUid(options.options.value("resource").value(0).toUtf8()); 68 auto resource = SinkshUtils::parseUid(options.options.value("resource").value(0).toUtf8());
@@ -236,7 +240,21 @@ bool inspect(const QStringList &args, State &state)
236 240
237Syntax::List syntax() 241Syntax::List syntax()
238{ 242{
239 Syntax state("inspect", QObject::tr("Inspect database for the resource requested"), &SinkInspect::inspect, Syntax::NotInteractive); 243 Syntax state("inspect", QObject::tr("Inspect database for the resource requested"),
244 &SinkInspect::inspect, Syntax::NotInteractive);
245
246 state.addParameter("resource",
247 { .name = "resource", .help = "Which resource to inspect", .required = true });
248 state.addParameter("db",
249 { .name = "database", .help = "Which database to inspect"});
250 state.addParameter("filter",
251 { .name = "id", .help = "A specific id to filter the results by (currently not working)"});
252 state.addFlag("showinternal", "Show internal fields only");
253 state.addParameter("validaterids",
254 { .name = "type", .help = "Validate remote Ids of the given type"});
255 state.addParameter("fulltext",
256 { .name = "id", .help = "If 'id' is not given, count the number of fulltext documents. Else, print the terms of the document with the given id"});
257
240 state.completer = &SinkshUtils::resourceCompleter; 258 state.completer = &SinkshUtils::resourceCompleter;
241 259
242 return Syntax::List() << state; 260 return Syntax::List() << state;
diff --git a/sinksh/syntax_modules/sink_list.cpp b/sinksh/syntax_modules/sink_list.cpp
index 32fae4f..eef188b 100644
--- a/sinksh/syntax_modules/sink_list.cpp
+++ b/sinksh/syntax_modules/sink_list.cpp
@@ -39,6 +39,8 @@
39namespace SinkList 39namespace SinkList
40{ 40{
41 41
42Syntax::List syntax();
43
42static QByteArray compressId(bool compress, const QByteArray &id) 44static QByteArray compressId(bool compress, const QByteArray &id)
43{ 45{
44 if (!compress) { 46 if (!compress) {
@@ -117,7 +119,7 @@ QStringList printToList(const Sink::ApplicationDomain::ApplicationDomainType &o,
117bool list(const QStringList &args_, State &state) 119bool list(const QStringList &args_, State &state)
118{ 120{
119 if (args_.isEmpty()) { 121 if (args_.isEmpty()) {
120 state.printError(QObject::tr("Options: $type [--resource $resource] [--compact] [--filter $property=$value] [--id $id] [--showall|--show $property] [--reduce $reduceProperty:$selectorProperty] [--sort $sortProperty] [--limit $count]")); 122 state.printError(syntax()[0].usage());
121 return false; 123 return false;
122 } 124 }
123 125
@@ -127,7 +129,7 @@ bool list(const QStringList &args_, State &state)
127 Sink::Query query; 129 Sink::Query query;
128 query.setId("list"); 130 query.setId("list");
129 if (!SinkshUtils::applyFilter(query, options)) { 131 if (!SinkshUtils::applyFilter(query, options)) {
130 state.printError(QObject::tr("Options: $type [--resource $resource] [--compact] [--filter $property=$value] [--showall|--show $property]")); 132 state.printError(syntax()[0].usage());
131 return false; 133 return false;
132 } 134 }
133 135
@@ -198,6 +200,18 @@ bool list(const QStringList &args_, State &state)
198Syntax::List syntax() 200Syntax::List syntax()
199{ 201{
200 Syntax list("list", QObject::tr("List all resources, or the contents of one or more resources."), &SinkList::list, Syntax::NotInteractive); 202 Syntax list("list", QObject::tr("List all resources, or the contents of one or more resources."), &SinkList::list, Syntax::NotInteractive);
203
204 list.addPositionalArgument({.name = "type", .help = "The type of content to list (resource, identity, account, mail, etc.)"});
205 list.addParameter("resource", { .name = "resource", .help = "List only the content of the given resource" });
206 list.addFlag("compact", "Use a compact view (reduces the size of IDs)");
207 list.addParameter("filter", { .name = "property=$value", .help = "Filter the results" });
208 list.addParameter("id", { .name = "id", .help = "List only the content with the given ID" });
209 list.addFlag("showall", "Show all properties");
210 list.addParameter("show", { .name = "property", .help = "Only show the given property" });
211 list.addParameter("reduce", { .name = "property:$selectorProperty", .help = "Combine the result with the same $property, sorted by $selectorProperty" });
212 list.addParameter("sort", { .name = "property", .help = "Sort the results according to the given property" });
213 list.addParameter("limit", { .name = "count", .help = "Limit the results" });
214
201 list.completer = &SinkshUtils::resourceOrTypeCompleter; 215 list.completer = &SinkshUtils::resourceOrTypeCompleter;
202 return Syntax::List() << list; 216 return Syntax::List() << list;
203} 217}
diff --git a/sinksh/syntax_modules/sink_livequery.cpp b/sinksh/syntax_modules/sink_livequery.cpp
index e9e85ec..2d8d5d0 100644
--- a/sinksh/syntax_modules/sink_livequery.cpp
+++ b/sinksh/syntax_modules/sink_livequery.cpp
@@ -38,10 +38,12 @@
38namespace SinkLiveQuery 38namespace SinkLiveQuery
39{ 39{
40 40
41Syntax::List syntax();
42
41bool livequery(const QStringList &args_, State &state) 43bool livequery(const QStringList &args_, State &state)
42{ 44{
43 if (args_.isEmpty()) { 45 if (args_.isEmpty()) {
44 state.printError(QObject::tr("Options: $type [--resource $resource] [--compact] [--filter $property=$value] [--id $id] [--showall|--show $property]")); 46 state.printError(syntax()[0].usage());
45 return false; 47 return false;
46 } 48 }
47 49
@@ -55,7 +57,7 @@ bool livequery(const QStringList &args_, State &state)
55 query.setId("livequery"); 57 query.setId("livequery");
56 query.setFlags(Sink::Query::LiveQuery); 58 query.setFlags(Sink::Query::LiveQuery);
57 if (!SinkshUtils::applyFilter(query, options)) { 59 if (!SinkshUtils::applyFilter(query, options)) {
58 state.printError(QObject::tr("Options: $type [--resource $resource] [--compact] [--filter $property=$value] [--showall|--show $property]")); 60 state.printError(syntax()[0].usage());
59 return false; 61 return false;
60 } 62 }
61 if (options.options.contains("resource")) { 63 if (options.options.contains("resource")) {
@@ -124,6 +126,15 @@ bool livequery(const QStringList &args_, State &state)
124Syntax::List syntax() 126Syntax::List syntax()
125{ 127{
126 Syntax list("livequery", QObject::tr("Run a livequery."), &SinkLiveQuery::livequery, Syntax::EventDriven); 128 Syntax list("livequery", QObject::tr("Run a livequery."), &SinkLiveQuery::livequery, Syntax::EventDriven);
129
130 list.addPositionalArgument({ .name = "type", .help = "The type to run the livequery on" });
131 list.addParameter("resource", { .name = "resource", .help = "Filter the livequery to the given resource" });
132 list.addFlag("compact", "Use a compact view (reduces the size of IDs)");
133 list.addParameter("filter", { .name = "property=$value", .help = "Filter the results" });
134 list.addParameter("id", { .name = "id", .help = "List only the content with the given ID" });
135 list.addFlag("showall", "Show all properties");
136 list.addParameter("show", { .name = "property", .help = "Only show the given property" });
137
127 list.completer = &SinkshUtils::resourceOrTypeCompleter; 138 list.completer = &SinkshUtils::resourceOrTypeCompleter;
128 return Syntax::List() << list; 139 return Syntax::List() << list;
129} 140}
diff --git a/sinksh/syntax_modules/sink_modify.cpp b/sinksh/syntax_modules/sink_modify.cpp
index 2579550..6cda5ab 100644
--- a/sinksh/syntax_modules/sink_modify.cpp
+++ b/sinksh/syntax_modules/sink_modify.cpp
@@ -38,20 +38,12 @@
38namespace SinkModify 38namespace SinkModify
39{ 39{
40 40
41Syntax::List syntax();
42
41bool modify(const QStringList &args, State &state) 43bool modify(const QStringList &args, State &state)
42{ 44{
43 if (args.isEmpty()) {
44 state.printError(QObject::tr("A type is required"), "sink_modify/02");
45 return false;
46 }
47
48 if (args.count() < 2) {
49 state.printError(QObject::tr("A resource ID is required to remove items"), "sink_modify/03");
50 return false;
51 }
52
53 if (args.count() < 3) { 45 if (args.count() < 3) {
54 state.printError(QObject::tr("An object ID is required to remove items"), "sink_modify/03"); 46 state.printError(syntax()[0].usage());
55 return false; 47 return false;
56 } 48 }
57 49
@@ -81,6 +73,7 @@ bool modify(const QStringList &args, State &state)
81bool resource(const QStringList &args, State &state) 73bool resource(const QStringList &args, State &state)
82{ 74{
83 if (args.isEmpty()) { 75 if (args.isEmpty()) {
76 // TODO: pass the syntax as parameter
84 state.printError(QObject::tr("A resource can not be modified without an id"), "sink_modify/01"); 77 state.printError(QObject::tr("A resource can not be modified without an id"), "sink_modify/01");
85 } 78 }
86 79
@@ -105,11 +98,21 @@ bool resource(const QStringList &args, State &state)
105 return true; 98 return true;
106} 99}
107 100
108
109Syntax::List syntax() 101Syntax::List syntax()
110{ 102{
111 Syntax modify("modify", QObject::tr("Modify items in a resource"), &SinkModify::modify); 103 Syntax modify("modify", QObject::tr("Modify items in a resource"), &SinkModify::modify);
104 modify.addPositionalArgument({ .name = "type", .help = "The type of entity to modify (mail, event, etc.)" });
105 modify.addPositionalArgument({ .name = "resourceId", .help = "The ID of the resource containing the entity" });
106 modify.addPositionalArgument({ .name = "objectId", .help = "The ID of the entity" });
107 modify.addPositionalArgument(
108 { .name = "key value", .help = "Attributes and values to modify", .required = false, .variadic = true });
109
112 Syntax resource("resource", QObject::tr("Modify a resource"), &SinkModify::resource);//, Syntax::EventDriven); 110 Syntax resource("resource", QObject::tr("Modify a resource"), &SinkModify::resource);//, Syntax::EventDriven);
111
112 resource.addPositionalArgument({ .name = "id", .help = "The ID of the resource" });
113 resource.addPositionalArgument(
114 { .name = "key value", .help = "Attributes and values to modify", .required = false, .variadic = true });
115
113 resource.completer = &SinkshUtils::resourceOrTypeCompleter; 116 resource.completer = &SinkshUtils::resourceOrTypeCompleter;
114 modify.children << resource; 117 modify.children << resource;
115 118
diff --git a/sinksh/syntax_modules/sink_remove.cpp b/sinksh/syntax_modules/sink_remove.cpp
index 6baa60f..999a889 100644
--- a/sinksh/syntax_modules/sink_remove.cpp
+++ b/sinksh/syntax_modules/sink_remove.cpp
@@ -37,20 +37,12 @@
37namespace SinkRemove 37namespace SinkRemove
38{ 38{
39 39
40Syntax::List syntax();
41
40bool remove(const QStringList &args, State &state) 42bool remove(const QStringList &args, State &state)
41{ 43{
42 if (args.isEmpty()) {
43 state.printError(QObject::tr("A type is required"), "sink_remove/02");
44 return false;
45 }
46
47 if (args.count() < 2) {
48 state.printError(QObject::tr("A resource ID is required to remove items"), "sink_remove/03");
49 return false;
50 }
51
52 if (args.count() < 3) { 44 if (args.count() < 3) {
53 state.printError(QObject::tr("An object ID is required to remove items"), "sink_remove/03"); 45 state.printError(syntax()[0].usage());
54 return false; 46 return false;
55 } 47 }
56 48
@@ -137,14 +129,24 @@ bool identity(const QStringList &args, State &state)
137 return true; 129 return true;
138} 130}
139 131
140
141Syntax::List syntax() 132Syntax::List syntax()
142{ 133{
143 Syntax remove("remove", QObject::tr("Remove items in a resource"), &SinkRemove::remove); 134 Syntax remove("remove", QObject::tr("Remove items in a resource"), &SinkRemove::remove);
135
136 remove.addPositionalArgument({ .name = "type", .help = "The type of entity to remove (mail, event, etc.)" });
137 remove.addPositionalArgument({ .name = "resourceId", .help = "The ID of the resource containing the entity" });
138 remove.addPositionalArgument({ .name = "objectId", .help = "The ID of the entity to remove" });
139
144 Syntax resource("resource", QObject::tr("Removes a resource"), &SinkRemove::resource, Syntax::NotInteractive); 140 Syntax resource("resource", QObject::tr("Removes a resource"), &SinkRemove::resource, Syntax::NotInteractive);
141 resource.addPositionalArgument({ .name = "id", .help = "The ID of the resource to remove" });
142 resource.completer = &SinkshUtils::resourceCompleter;
143
145 Syntax account("account", QObject::tr("Removes a account"), &SinkRemove::account, Syntax::NotInteractive); 144 Syntax account("account", QObject::tr("Removes a account"), &SinkRemove::account, Syntax::NotInteractive);
145 account.addPositionalArgument({ .name = "id", .help = "The ID of the account to remove" });
146
146 Syntax identity("identity", QObject::tr("Removes an identity"), &SinkRemove::identity, Syntax::NotInteractive); 147 Syntax identity("identity", QObject::tr("Removes an identity"), &SinkRemove::identity, Syntax::NotInteractive);
147 resource.completer = &SinkshUtils::resourceCompleter; 148 identity.addPositionalArgument({ .name = "id", .help = "The ID of the account to remove" });
149
148 remove.children << resource << account << identity; 150 remove.children << resource << account << identity;
149 151
150 return Syntax::List() << remove; 152 return Syntax::List() << remove;
diff --git a/sinksh/syntax_modules/sink_stat.cpp b/sinksh/syntax_modules/sink_stat.cpp
index 7263941..a936af2 100644
--- a/sinksh/syntax_modules/sink_stat.cpp
+++ b/sinksh/syntax_modules/sink_stat.cpp
@@ -69,7 +69,6 @@ void statResource(const QString &resource, const State &state)
69 state.printLine(QObject::tr("Actual database file sizes total: %1 [kb]").arg(size), 1); 69 state.printLine(QObject::tr("Actual database file sizes total: %1 [kb]").arg(size), 1);
70 70
71 QDir dataDir{Sink::resourceStorageLocation(resource.toLatin1()) + "/fulltext/"}; 71 QDir dataDir{Sink::resourceStorageLocation(resource.toLatin1()) + "/fulltext/"};
72 Q_ASSERT(dataDir.exists());
73 qint64 dataSize = 0; 72 qint64 dataSize = 0;
74 for (const auto &e : dataDir.entryInfoList(QDir::Files | QDir::NoSymLinks | QDir::NoDotAndDotDot)) { 73 for (const auto &e : dataDir.entryInfoList(QDir::Files | QDir::NoSymLinks | QDir::NoDotAndDotDot)) {
75 dataSize += e.size(); 74 dataSize += e.size();
@@ -102,7 +101,15 @@ bool stat(const QStringList &args, State &state)
102 101
103Syntax::List syntax() 102Syntax::List syntax()
104{ 103{
105 Syntax state("stat", QObject::tr("Shows database usage for the resources requested"), &SinkStat::stat, Syntax::NotInteractive); 104 Syntax state("stat", QObject::tr("Shows database usage for the resources requested"),
105 &SinkStat::stat, Syntax::NotInteractive);
106
107 state.addPositionalArgument({ .name = "resourceId",
108 .help = "Show statistics of the given resource(s). If no resource is provided, show "
109 "statistics of all resources",
110 .required = false,
111 .variadic = true });
112
106 state.completer = &SinkshUtils::resourceCompleter; 113 state.completer = &SinkshUtils::resourceCompleter;
107 114
108 return Syntax::List() << state; 115 return Syntax::List() << state;
diff --git a/sinksh/syntax_modules/sink_sync.cpp b/sinksh/syntax_modules/sink_sync.cpp
index f165f58..e13e8f8 100644
--- a/sinksh/syntax_modules/sink_sync.cpp
+++ b/sinksh/syntax_modules/sink_sync.cpp
@@ -83,6 +83,11 @@ bool sync(const QStringList &args, State &state)
83Syntax::List syntax() 83Syntax::List syntax()
84{ 84{
85 Syntax sync("sync", QObject::tr("Synchronizes a resource."), &SinkSync::sync, Syntax::EventDriven); 85 Syntax sync("sync", QObject::tr("Synchronizes a resource."), &SinkSync::sync, Syntax::EventDriven);
86
87 sync.addPositionalArgument({ .name = "type", .help = "The type of resource to synchronize" });
88 sync.addPositionalArgument({ .name = "resourceId", .help = "The ID of the resource to synchronize" });
89 sync.addParameter("password", { .name = "password", .help = "The password of the resource", .required = true });
90
86 sync.completer = &SinkshUtils::resourceCompleter; 91 sync.completer = &SinkshUtils::resourceCompleter;
87 92
88 return Syntax::List() << sync; 93 return Syntax::List() << sync;
diff --git a/sinksh/syntax_modules/sink_trace.cpp b/sinksh/syntax_modules/sink_trace.cpp
index ed5e2d8..2811258 100644
--- a/sinksh/syntax_modules/sink_trace.cpp
+++ b/sinksh/syntax_modules/sink_trace.cpp
@@ -66,18 +66,17 @@ bool trace(const QStringList &args, State &state)
66 return traceOn(args, state); 66 return traceOn(args, state);
67} 67}
68 68
69
70Syntax::List syntax() 69Syntax::List syntax()
71{ 70{
72 Syntax trace("trace", QObject::tr("Control trace debug output."), &SinkTrace::trace, Syntax::NotInteractive); 71 Syntax trace("trace", QObject::tr("Control trace debug output."), &SinkTrace::trace, Syntax::NotInteractive);
73 trace.completer = &SinkshUtils::debugareaCompleter; 72 trace.completer = &SinkshUtils::debugareaCompleter;
74 73
75 Syntax traceOff("off", QObject::tr("Turns off trace output."), &SinkTrace::traceOff, Syntax::NotInteractive); 74 Syntax traceOff("off", QObject::tr("Turns off trace output."), &SinkTrace::traceOff, Syntax::NotInteractive);
76 traceOff.completer = &SinkshUtils::debugareaCompleter; 75 traceOff.completer = &SinkshUtils::debugareaCompleter;
77 trace.children << traceOff; 76 trace.children << traceOff;
78 77
79 Syntax traceOn("on", QObject::tr("Turns on trace output."), &SinkTrace::traceOn, Syntax::NotInteractive); 78 Syntax traceOn("on", QObject::tr("Turns on trace output."), &SinkTrace::traceOn, Syntax::NotInteractive);
80 traceOn.completer = &SinkshUtils::debugareaCompleter; 79 traceOn.completer = &SinkshUtils::debugareaCompleter;
81 trace.children << traceOn; 80 trace.children << traceOn;
82 81
83 return Syntax::List() << trace; 82 return Syntax::List() << trace;
diff --git a/sinksh/syntaxtree.cpp b/sinksh/syntaxtree.cpp
index 0eb9782..fea99ef 100644
--- a/sinksh/syntaxtree.cpp
+++ b/sinksh/syntaxtree.cpp
@@ -33,6 +33,104 @@ Syntax::Syntax(const QString &k, const QString &helpText, std::function<bool(con
33{ 33{
34} 34}
35 35
36void Syntax::addPositionalArgument(const Argument &argument)
37{
38 arguments.push_back(argument);
39}
40
41void Syntax::addParameter(const QString &name, const ParameterOptions &options)
42{
43 parameters.insert(name, options);
44}
45
46void Syntax::addFlag(const QString &name, const QString &help)
47{
48 flags.insert(name, help);
49}
50
51QString Syntax::usage() const
52{
53 // TODO: refactor into meaningful functions?
54 bool hasArguments = !arguments.isEmpty();
55 bool hasFlags = !flags.isEmpty();
56 bool hasOptions = !parameters.isEmpty();
57 bool hasSubcommand = !children.isEmpty();
58
59 QString argumentsSummary;
60
61 QString argumentsUsage;
62 if (hasArguments) {
63 argumentsUsage += "\nARGUMENTS:\n";
64 for (const auto &arg : arguments) {
65 if (arg.required) {
66 argumentsSummary += QString(" <%1>").arg(arg.name);
67 argumentsUsage += QString(" <%1>: %2\n").arg(arg.name).arg(arg.help);
68 } else {
69 argumentsSummary += QString(" [%1]").arg(arg.name);
70 argumentsUsage += QString(" [%1]: %2\n").arg(arg.name).arg(arg.help);
71 }
72 if (arg.variadic) {
73 argumentsSummary += "...";
74 }
75 }
76 }
77
78 if (hasFlags) {
79 argumentsSummary += " [FLAGS]";
80 }
81
82 if (hasOptions) {
83 argumentsSummary += " [OPTIONS]";
84 }
85
86 if (hasSubcommand) {
87 if (hasArguments || hasFlags || hasOptions) {
88 argumentsSummary = QString(" [ <SUB-COMMAND> |%1 ]").arg(argumentsSummary);
89 } else {
90 argumentsSummary = " <SUB-COMMAND>";
91 }
92 }
93
94 argumentsSummary += '\n';
95
96 QString subcommandsUsage;
97 if (hasSubcommand) {
98 subcommandsUsage += "\nSUB-COMMANDS:\n"
99 " Use the 'help' command to find out more about a sub-command.\n\n";
100 for (const auto &command : children) {
101 subcommandsUsage += QString(" %1: %2\n").arg(command.keyword).arg(command.help);
102 }
103 }
104
105 QString flagsUsage;
106 if (hasFlags) {
107 flagsUsage += "\nFLAGS:\n";
108 for (auto it = flags.constBegin(); it != flags.constEnd(); ++it) {
109 flagsUsage += QString(" [--%1]: %2\n").arg(it.key()).arg(it.value());
110 }
111 }
112
113 QString optionsUsage;
114 if (hasOptions) {
115 optionsUsage += "\nOPTIONS:\n";
116 for (auto it = parameters.constBegin(); it != parameters.constEnd(); ++it) {
117 optionsUsage += " ";
118 if (!it.value().required) {
119 optionsUsage += QString("[--%1 $%2]").arg(it.key()).arg(it.value().name);
120 } else {
121 optionsUsage += QString("<--%1 $%2>").arg(it.key()).arg(it.value().name);
122 }
123
124 optionsUsage += ": " + it.value().help + '\n';
125 }
126 }
127
128 // TODO: instead of just the keyword, we might want to have the whole
129 // command (e.g. if this is a sub-command)
130 return QString("USAGE:\n ") + keyword + argumentsSummary + subcommandsUsage +
131 argumentsUsage + flagsUsage + optionsUsage;
132}
133
36SyntaxTree::SyntaxTree() 134SyntaxTree::SyntaxTree()
37{ 135{
38} 136}
diff --git a/sinksh/syntaxtree.h b/sinksh/syntaxtree.h
index ce28548..3d90288 100644
--- a/sinksh/syntaxtree.h
+++ b/sinksh/syntaxtree.h
@@ -42,10 +42,33 @@ public:
42 Syntax(const QString &keyword, const QString &helpText = QString(), 42 Syntax(const QString &keyword, const QString &helpText = QString(),
43 std::function<bool(const QStringList &, State &)> lambda = std::function<bool(const QStringList &, State &)>(), Interactivity interactivity = NotInteractive); 43 std::function<bool(const QStringList &, State &)> lambda = std::function<bool(const QStringList &, State &)>(), Interactivity interactivity = NotInteractive);
44 44
45 struct Argument {
46 QString name;
47 QString help;
48 bool required = true;
49 bool variadic = false;
50 };
51
52 struct ParameterOptions {
53 QString name;
54 QString help;
55 bool required = false;
56 };
57
58 // TODO: add examples?
45 QString keyword; 59 QString keyword;
46 QString help; 60 QString help;
61 QVector<Argument> arguments;
62 QMap<QString, ParameterOptions> parameters;
63 QMap<QString, QString> flags;
47 Interactivity interactivity; 64 Interactivity interactivity;
48 65
66 void addPositionalArgument(const Argument &);
67 void addParameter(const QString &name, const ParameterOptions &options);
68 void addFlag(const QString &name, const QString &help);
69
70 QString usage() const;
71
49 /** 72 /**
50 * This function will be called to execute the command. 73 * This function will be called to execute the command.
51 * 74 *