diff options
author | Rémi Nicole <nicole@kolabsystems.com> | 2018-08-03 16:59:16 +0200 |
---|---|---|
committer | Christian Mollekopf <chrigi_1@fastmail.fm> | 2018-08-03 17:10:31 +0200 |
commit | 50bed81038f10091d35c5719df8078612393ae95 (patch) | |
tree | e4a3b634118b2b1b9fd88203902e934299deac9b /sinksh | |
parent | ad3dc221273100ba61828f993f1d1c8a1272d665 (diff) | |
download | sink-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
Diffstat (limited to 'sinksh')
-rw-r--r-- | sinksh/syntax_modules/core_syntax.cpp | 14 | ||||
-rw-r--r-- | sinksh/syntax_modules/sink_clear.cpp | 7 | ||||
-rw-r--r-- | sinksh/syntax_modules/sink_count.cpp | 10 | ||||
-rw-r--r-- | sinksh/syntax_modules/sink_create.cpp | 35 | ||||
-rw-r--r-- | sinksh/syntax_modules/sink_drop.cpp | 6 | ||||
-rw-r--r-- | sinksh/syntax_modules/sink_inspect.cpp | 22 | ||||
-rw-r--r-- | sinksh/syntax_modules/sink_list.cpp | 18 | ||||
-rw-r--r-- | sinksh/syntax_modules/sink_livequery.cpp | 15 | ||||
-rw-r--r-- | sinksh/syntax_modules/sink_modify.cpp | 27 | ||||
-rw-r--r-- | sinksh/syntax_modules/sink_remove.cpp | 28 | ||||
-rw-r--r-- | sinksh/syntax_modules/sink_stat.cpp | 11 | ||||
-rw-r--r-- | sinksh/syntax_modules/sink_sync.cpp | 5 | ||||
-rw-r--r-- | sinksh/syntax_modules/sink_trace.cpp | 7 | ||||
-rw-r--r-- | sinksh/syntaxtree.cpp | 98 | ||||
-rw-r--r-- | sinksh/syntaxtree.h | 23 |
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 @@ | |||
35 | namespace SinkClear | 35 | namespace SinkClear |
36 | { | 36 | { |
37 | 37 | ||
38 | Syntax::List syntax(); | ||
39 | |||
38 | bool clear(const QStringList &args, State &state) | 40 | bool 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) | |||
53 | Syntax::List syntax() | 55 | Syntax::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 @@ | |||
37 | namespace SinkCount | 37 | namespace SinkCount |
38 | { | 38 | { |
39 | 39 | ||
40 | Syntax::List syntax(); | ||
41 | |||
40 | bool count(const QStringList &args, State &state) | 42 | bool 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 | ||
64 | Syntax::List syntax() | 66 | Syntax::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; | |||
40 | namespace SinkCreate | 40 | namespace SinkCreate |
41 | { | 41 | { |
42 | 42 | ||
43 | Syntax::List syntax(); | ||
44 | |||
43 | bool create(const QStringList &allArgs, State &state) | 45 | bool 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 | |||
177 | Syntax::List syntax() | 173 | Syntax::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 @@ | |||
35 | namespace SinkDrop | 35 | namespace SinkDrop |
36 | { | 36 | { |
37 | 37 | ||
38 | Syntax::List syntax(); | ||
39 | |||
38 | bool drop(const QStringList &args, State &state) | 40 | bool 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) | |||
60 | Syntax::List syntax() | 62 | Syntax::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 | ||
58 | Syntax::List syntax(); | ||
59 | |||
58 | bool inspect(const QStringList &args, State &state) | 60 | bool 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 | ||
237 | Syntax::List syntax() | 241 | Syntax::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 @@ | |||
39 | namespace SinkList | 39 | namespace SinkList |
40 | { | 40 | { |
41 | 41 | ||
42 | Syntax::List syntax(); | ||
43 | |||
42 | static QByteArray compressId(bool compress, const QByteArray &id) | 44 | static 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, | |||
117 | bool list(const QStringList &args_, State &state) | 119 | bool 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) | |||
198 | Syntax::List syntax() | 200 | Syntax::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 @@ | |||
38 | namespace SinkLiveQuery | 38 | namespace SinkLiveQuery |
39 | { | 39 | { |
40 | 40 | ||
41 | Syntax::List syntax(); | ||
42 | |||
41 | bool livequery(const QStringList &args_, State &state) | 43 | bool 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) | |||
124 | Syntax::List syntax() | 126 | Syntax::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 @@ | |||
38 | namespace SinkModify | 38 | namespace SinkModify |
39 | { | 39 | { |
40 | 40 | ||
41 | Syntax::List syntax(); | ||
42 | |||
41 | bool modify(const QStringList &args, State &state) | 43 | bool 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) | |||
81 | bool resource(const QStringList &args, State &state) | 73 | bool 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 | |||
109 | Syntax::List syntax() | 101 | Syntax::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 @@ | |||
37 | namespace SinkRemove | 37 | namespace SinkRemove |
38 | { | 38 | { |
39 | 39 | ||
40 | Syntax::List syntax(); | ||
41 | |||
40 | bool remove(const QStringList &args, State &state) | 42 | bool 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 | |||
141 | Syntax::List syntax() | 132 | Syntax::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 | ||
103 | Syntax::List syntax() | 102 | Syntax::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) | |||
83 | Syntax::List syntax() | 83 | Syntax::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 | |||
70 | Syntax::List syntax() | 69 | Syntax::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 | ||
36 | void Syntax::addPositionalArgument(const Argument &argument) | ||
37 | { | ||
38 | arguments.push_back(argument); | ||
39 | } | ||
40 | |||
41 | void Syntax::addParameter(const QString &name, const ParameterOptions &options) | ||
42 | { | ||
43 | parameters.insert(name, options); | ||
44 | } | ||
45 | |||
46 | void Syntax::addFlag(const QString &name, const QString &help) | ||
47 | { | ||
48 | flags.insert(name, help); | ||
49 | } | ||
50 | |||
51 | QString 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 | |||
36 | SyntaxTree::SyntaxTree() | 134 | SyntaxTree::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 | * |