summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAaron Seigo <aseigo@kde.org>2015-12-23 11:43:37 +0100
committerAaron Seigo <aseigo@kde.org>2015-12-23 11:43:37 +0100
commit071f4ef0122a8bfceeda9a10b41e85ad9a34a28d (patch)
treee5d1cb7efcac5eb16f4848619ff06142906b6cea
parent255d73af197faf8437343abc10bd98cca2057a1e (diff)
downloadsink-071f4ef0122a8bfceeda9a10b41e85ad9a34a28d.tar.gz
sink-071f4ef0122a8bfceeda9a10b41e85ad9a34a28d.zip
vastly simplify by getting rid of Module as a base class
just a move slightly more towards functional
-rw-r--r--akonadi2_cli/CMakeLists.txt11
-rw-r--r--akonadi2_cli/main.cpp5
-rw-r--r--akonadi2_cli/module.cpp116
-rw-r--r--akonadi2_cli/module.h41
-rw-r--r--akonadi2_cli/modules/core_syntax.cpp (renamed from akonadi2_cli/modules/help/help.cpp)33
-rw-r--r--akonadi2_cli/modules/core_syntax.h (renamed from akonadi2_cli/modules/exit/exit.h)17
-rw-r--r--akonadi2_cli/modules/exit/exit.cpp39
-rw-r--r--akonadi2_cli/modules/help/help.h37
-rw-r--r--akonadi2_cli/repl/replStates.cpp4
9 files changed, 100 insertions, 203 deletions
diff --git a/akonadi2_cli/CMakeLists.txt b/akonadi2_cli/CMakeLists.txt
index b5bdb46..a07140e 100644
--- a/akonadi2_cli/CMakeLists.txt
+++ b/akonadi2_cli/CMakeLists.txt
@@ -3,14 +3,17 @@ project(akonadi2_cli)
3find_package(Readline REQUIRED) 3find_package(Readline REQUIRED)
4 4
5 5
6set(akonadi2_SRCS 6set(akonadi2_cli_SRCS
7 main.cpp 7 main.cpp
8 module.cpp 8 module.cpp
9 modules/exit/exit.cpp 9 modules/core_syntax.cpp
10 modules/help/help.cpp
11 repl/repl.cpp 10 repl/repl.cpp
12 repl/replStates.cpp 11 repl/replStates.cpp
13 state.cpp) 12 state.cpp)
14 13
15add_executable(${PROJECT_NAME} ${akonadi2_SRCS}) 14include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
15
16add_executable(${PROJECT_NAME} ${akonadi2_cli_SRCS})
17target_link_libraries(${PROJECT_NAME} Qt5::Core ${Readline_LIBRARY})
18install(TARGETS ${PROJECT_NAME} ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
16 19
diff --git a/akonadi2_cli/main.cpp b/akonadi2_cli/main.cpp
index 1a036d0..d23e070 100644
--- a/akonadi2_cli/main.cpp
+++ b/akonadi2_cli/main.cpp
@@ -36,9 +36,6 @@
36 36
37int main(int argc, char *argv[]) 37int main(int argc, char *argv[])
38{ 38{
39 // load all modules
40 Module::loadModules();
41
42 const bool interactive = isatty(fileno(stdin)); 39 const bool interactive = isatty(fileno(stdin));
43 const bool startRepl = (argc == 1) && interactive; 40 const bool startRepl = (argc == 1) && interactive;
44 //TODO: make a json command parse cause that would be awesomesauce 41 //TODO: make a json command parse cause that would be awesomesauce
@@ -67,5 +64,5 @@ int main(int argc, char *argv[])
67 64
68 QStringList commands = app.arguments(); 65 QStringList commands = app.arguments();
69 commands.removeFirst(); 66 commands.removeFirst();
70 return Module::run(commands); 67 return Module::self()->run(commands);
71} 68}
diff --git a/akonadi2_cli/module.cpp b/akonadi2_cli/module.cpp
index 0b83d98..dfc58c8 100644
--- a/akonadi2_cli/module.cpp
+++ b/akonadi2_cli/module.cpp
@@ -24,83 +24,61 @@
24 24
25// TODO: needs a proper registry; making "core" modules plugins is 25// TODO: needs a proper registry; making "core" modules plugins is
26// almost certainly overkill, but this is not the way either 26// almost certainly overkill, but this is not the way either
27#include "modules/exit/exit.h" 27#include "modules/core_syntax.h"
28#include "modules/help/help.h"
29 28
30QList<Module> Module::s_modules; 29Module *Module::s_module = 0;
31State Module::s_state;
32 30
33Module::Syntax::Syntax() 31Module::Syntax::Syntax()
34{ 32{
35} 33}
36 34
37Module::Syntax::Syntax(const QString &k, std::function<bool(const QStringList &, State &)> l, const QString &helpText, bool e) 35Module::Syntax::Syntax(const QString &k, const QString &helpText, std::function<bool(const QStringList &, State &)> l, Interactivity inter)
38 : keyword(k), 36 : keyword(k),
39 lambda(l),
40 help(helpText), 37 help(helpText),
41 eventDriven(e) 38 interactivity(inter),
39 lambda(l)
42{ 40{
43} 41}
44 42
45Module::Module() 43Module::Module()
46{ 44{
45 QVector<std::function<SyntaxList()> > syntaxModules;
46 syntaxModules << &CoreSyntax::syntax;
47 for (auto syntaxModule: syntaxModules) {
48 m_syntax += syntaxModule();
49 }
47} 50}
48 51
49void Module::loadModules() 52Module *Module::self()
50{
51 addModule(CLI::Exit());
52 addModule(CLI::Help());
53}
54
55void Module::addModule(const Module &module)
56{
57 s_modules.append(module);
58}
59
60QList<Module> Module::modules()
61{
62 return s_modules;
63}
64
65Module::Command Module::match(const QStringList &commands)
66{ 53{
67 Command command; 54 if (!s_module) {
68 for (const Module &module: s_modules) { 55 s_module = new Module;
69 command = module.matches(commands);
70 if (command.first) {
71 break;
72 }
73 } 56 }
74 57
75 return command; 58 return s_module;
76} 59}
77 60
78Module::Syntax Module::syntax() const 61Module::SyntaxList Module::syntax() const
79{ 62{
80 return m_syntax; 63 return m_syntax;
81} 64}
82 65
83void Module::setSyntax(const Syntax &syntax)
84{
85 m_syntax = syntax;
86}
87
88bool Module::run(const QStringList &commands) 66bool Module::run(const QStringList &commands)
89{ 67{
90 Command command = match(commands); 68 Command command = match(commands);
91 if (command.first && command.first->lambda) { 69 if (command.first && command.first->lambda) {
92 bool rv = command.first->lambda(command.second, s_state); 70 command.first->lambda(command.second, m_state);
93 if (rv && command.first->eventDriven) { 71 if (command.first->interactivity == Syntax::EventDriven) {
94 return QCoreApplication::instance()->exec(); 72 return QCoreApplication::instance()->exec();
95 } 73 }
96 74
97 return rv; 75 return true;
98 } 76 }
99 77
100 return false; 78 return false;
101} 79}
102 80
103Module::Command Module::matches(const QStringList &commandLine) const 81Module::Command Module::match(const QStringList &commandLine) const
104{ 82{
105 if (commandLine.isEmpty()) { 83 if (commandLine.isEmpty()) {
106 return Command(); 84 return Command();
@@ -108,20 +86,16 @@ Module::Command Module::matches(const QStringList &commandLine) const
108 86
109 QStringListIterator commandLineIt(commandLine); 87 QStringListIterator commandLineIt(commandLine);
110 88
111 if (commandLineIt.next() != m_syntax.keyword) { 89 QVectorIterator<Syntax> syntaxIt(m_syntax);
112 return Command(); 90 const Syntax *lastFullSyntax = 0;
113 }
114
115 QListIterator<Syntax> syntaxIt(m_syntax.children);
116 const Syntax *syntax = &m_syntax;
117 QStringList tailCommands; 91 QStringList tailCommands;
118 while (commandLineIt.hasNext() && syntaxIt.hasNext()) { 92 while (commandLineIt.hasNext() && syntaxIt.hasNext()) {
119 const QString word = commandLineIt.next(); 93 const QString word = commandLineIt.next();
120 while (syntaxIt.hasNext()) { 94 while (syntaxIt.hasNext()) {
121 const Syntax &child = syntaxIt.next(); 95 const Syntax &syntax = syntaxIt.next();
122 if (word == child.keyword) { 96 if (word == syntax.keyword) {
123 syntax = &child; 97 lastFullSyntax = &syntax;
124 syntaxIt = child.children; 98 syntaxIt = syntax.children;
125 } 99 }
126 } 100 }
127 101
@@ -131,55 +105,47 @@ Module::Command Module::matches(const QStringList &commandLine) const
131 } 105 }
132 } 106 }
133 107
134 if (syntax && syntax->lambda) { 108 if (lastFullSyntax && lastFullSyntax->lambda) {
135 while (commandLineIt.hasNext()) { 109 while (commandLineIt.hasNext()) {
136 tailCommands << commandLineIt.next(); 110 tailCommands << commandLineIt.next();
137 } 111 }
138 112
139 return std::make_pair(syntax, tailCommands); 113 return std::make_pair(lastFullSyntax, tailCommands);
140 } 114 }
141 115
142 return Command(); 116 return Command();
143} 117}
144 118
145QVector<Module::Syntax> Module::nearestSyntax(const QStringList &words, const QString &fragment) 119Module::SyntaxList Module::nearestSyntax(const QStringList &words, const QString &fragment) const
146{ 120{
147 QVector<Module::Syntax> matches; 121 SyntaxList matches;
148 122
149 //qDebug() << "words are" << words; 123 //qDebug() << "words are" << words;
150 if (words.isEmpty()) { 124 if (words.isEmpty()) {
151 for (const Module &module: s_modules) { 125 for (const Syntax &syntax: m_syntax) {
152 if (module.syntax().keyword.startsWith(fragment)) { 126 if (syntax.keyword.startsWith(fragment)) {
153 matches.push_back(module.syntax()); 127 matches.push_back(syntax);
154 } 128 }
155 } 129 }
156 } else { 130 } else {
157 QStringListIterator wordIt(words); 131 QStringListIterator wordIt(words);
158 QString word = wordIt.next(); 132 QVectorIterator<Syntax> syntaxIt(m_syntax);
159 Syntax lastFullSyntax; 133 Syntax lastFullSyntax;
160 134
161 for (const Module &module: s_modules) { 135 while (wordIt.hasNext()) {
162 if (module.syntax().keyword == word) { 136 QString word = wordIt.next();
163 lastFullSyntax = module.syntax(); 137 while (syntaxIt.hasNext()) {
164 QListIterator<Syntax> syntaxIt(module.syntax().children); 138 const Syntax &syntax = syntaxIt.next();
165 while (wordIt.hasNext()) { 139 if (word == syntax.keyword) {
166 word = wordIt.next(); 140 lastFullSyntax = syntax;
167 while (syntaxIt.hasNext()) { 141 syntaxIt = syntax.children;
168 const Syntax &child = syntaxIt.next();
169 if (word == child.keyword) {
170 lastFullSyntax = child;
171 syntaxIt = child.children;
172 }
173 }
174 } 142 }
175
176 break;
177 } 143 }
178 } 144 }
179 145
180 //qDebug() << "exiting with" << lastFullSyntax.keyword << words.last(); 146 //qDebug() << "exiting with" << lastFullSyntax.keyword << words.last();
181 if (lastFullSyntax.keyword == words.last()) { 147 if (lastFullSyntax.keyword == words.last()) {
182 QListIterator<Syntax> syntaxIt(lastFullSyntax.children); 148 syntaxIt = lastFullSyntax.children;
183 while (syntaxIt.hasNext()) { 149 while (syntaxIt.hasNext()) {
184 Syntax syntax = syntaxIt.next(); 150 Syntax syntax = syntaxIt.next();
185 if (fragment.isEmpty() || syntax.keyword.startsWith(fragment)) { 151 if (fragment.isEmpty() || syntax.keyword.startsWith(fragment)) {
diff --git a/akonadi2_cli/module.h b/akonadi2_cli/module.h
index 4f426b8..d2745d0 100644
--- a/akonadi2_cli/module.h
+++ b/akonadi2_cli/module.h
@@ -29,33 +29,42 @@ class Module
29public: 29public:
30 struct Syntax 30 struct Syntax
31 { 31 {
32 enum Interactivity {
33 NotInteractive = 0,
34 EventDriven
35 };
36
32 Syntax(); 37 Syntax();
33 Syntax(const QString &keyword, std::function<bool(const QStringList &, State &)> lambda = std::function<bool(const QStringList &, State &)>(), const QString &helpText = QString(), bool eventDriven = false); 38 Syntax(const QString &keyword,
39 const QString &helpText = QString(),
40 std::function<bool(const QStringList &, State &)> lambda = std::function<bool(const QStringList &, State &)>(),
41 Interactivity interactivity = NotInteractive);
42
34 QString keyword; 43 QString keyword;
35 std::function<bool(const QStringList &, State &)> lambda;
36 QList<Syntax> children;
37 QString help; 44 QString help;
38 bool eventDriven; 45 Interactivity interactivity;
46 std::function<bool(const QStringList &, State &)> lambda;
47
48 QVector<Syntax> children;
39 }; 49 };
40 50
41 typedef std::pair<const Syntax *, QStringList> Command; 51 typedef std::pair<const Syntax *, QStringList> Command;
52 typedef QVector<Module::Syntax> SyntaxList;
42 53
43 static void addModule(const Module &module); 54 static Module *self();
44 static QList<Module> modules();
45 static Command match(const QStringList &commands);
46 static bool run(const QStringList &commands);
47 static void loadModules();
48 static QVector<Syntax>nearestSyntax(const QStringList &words, const QString &fragment);
49 55
50 Module(); 56 SyntaxList syntax() const;
51 Module::Syntax syntax() const; 57 Command match(const QStringList &commands) const;
52 void setSyntax(const Syntax &syntax); 58 SyntaxList nearestSyntax(const QStringList &words, const QString &fragment) const;
59
60 bool run(const QStringList &commands);
53 61
54private: 62private:
63 Module();
55 Command matches(const QStringList &commands) const; 64 Command matches(const QStringList &commands) const;
56 65
57 Syntax m_syntax; 66 SyntaxList m_syntax;
58 static QList<Module> s_modules; 67 State m_state;
59 static State s_state; 68 static Module *s_module;
60}; 69};
61 70
diff --git a/akonadi2_cli/modules/help/help.cpp b/akonadi2_cli/modules/core_syntax.cpp
index aaff6fb..8324c31 100644
--- a/akonadi2_cli/modules/help/help.cpp
+++ b/akonadi2_cli/modules/core_syntax.cpp
@@ -17,39 +17,45 @@
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */ 18 */
19 19
20#include "help.h" 20#include "core_syntax.h"
21 21
22#include <QObject> 22#include <QObject> // tr()
23#include <QSet> 23#include <QSet>
24#include <QTextStream> 24#include <QTextStream>
25 25
26#include "module.h" 26namespace CoreSyntax
27{
27 28
28namespace CLI 29Module::SyntaxList syntax()
29{ 30{
31 Module::SyntaxList syntax;
32 syntax << Module::Syntax("exit", QObject::tr("Exits the application. Ctrl-d also works!"), &CoreSyntax::exit);
33 syntax << Module::Syntax(QObject::tr("help"), QObject::tr("Print command information: help [command]"), &CoreSyntax::showHelp);
34 return syntax;
35}
30 36
31Help::Help() 37bool exit(const QStringList &, State &)
32{ 38{
33 Syntax topLevel = Syntax(QObject::tr("help"), &Help::showHelp, QObject::tr("Print command information: help [command]")); 39 ::exit(0);
34 setSyntax(topLevel); 40 return true;
35} 41}
36 42
37bool Help::showHelp(const QStringList &commands, State &) 43bool showHelp(const QStringList &commands, State &)
38{ 44{
39 Module::Command command = Module::match(commands); 45 Module::Command command = Module::self()->match(commands);
40 QTextStream stream(stdout); 46 QTextStream stream(stdout);
41 if (commands.isEmpty()) { 47 if (commands.isEmpty()) {
42 stream << QObject::tr("Welcome to the Akonadi2 command line tool!") << "\n"; 48 stream << QObject::tr("Welcome to the Akonadi2 command line tool!") << "\n";
43 stream << QObject::tr("Top-level commands:") << "\n"; 49 stream << QObject::tr("Top-level commands:") << "\n";
44 QSet<QString> sorted; 50 QSet<QString> sorted;
45 for (auto module: Module::modules()) { 51 for (auto syntax: Module::self()->syntax()) {
46 sorted.insert(module.syntax().keyword); 52 sorted.insert(syntax.keyword);
47 } 53 }
48 54
49 for (auto keyword: sorted) { 55 for (auto keyword: sorted) {
50 stream << "\t" << keyword << "\n"; 56 stream << "\t" << keyword << "\n";
51 } 57 }
52 } else if (const Syntax *syntax = command.first) { 58 } else if (const Module::Syntax *syntax = command.first) {
53 //TODO: get parent! 59 //TODO: get parent!
54 stream << QObject::tr("Command `%1`").arg(syntax->keyword); 60 stream << QObject::tr("Command `%1`").arg(syntax->keyword);
55 61
@@ -72,9 +78,8 @@ bool Help::showHelp(const QStringList &commands, State &)
72 } else { 78 } else {
73 stream << "Unknown command: " << commands.join(" ") << "\n"; 79 stream << "Unknown command: " << commands.join(" ") << "\n";
74 } 80 }
75
76 return true; 81 return true;
77} 82}
78 83
79} // namespace CLI 84} // namespace CoreSyntax
80 85
diff --git a/akonadi2_cli/modules/exit/exit.h b/akonadi2_cli/modules/core_syntax.h
index 5ed4174..beb8528 100644
--- a/akonadi2_cli/modules/exit/exit.h
+++ b/akonadi2_cli/modules/core_syntax.h
@@ -21,17 +21,10 @@
21 21
22#include "module.h" 22#include "module.h"
23 23
24namespace CLI 24namespace CoreSyntax
25{ 25{
26 26 Module::SyntaxList syntax();
27class Exit : public Module 27 bool exit(const QStringList &commands, State &state);
28{ 28 bool showHelp(const QStringList &commands, State &);
29public: 29}
30 Exit();
31
32private:
33 static bool exit(const QStringList &commands, State &state);
34};
35
36} // namespace CLI
37 30
diff --git a/akonadi2_cli/modules/exit/exit.cpp b/akonadi2_cli/modules/exit/exit.cpp
deleted file mode 100644
index 64828be..0000000
--- a/akonadi2_cli/modules/exit/exit.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
1/*
2 * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#include "exit.h"
21
22#include <QObject>
23
24namespace CLI
25{
26
27Exit::Exit()
28{
29 setSyntax(Syntax("exit", &Exit::exit, QObject::tr("Exits the application. Ctrl-d also works!")));
30}
31
32bool Exit::exit(const QStringList &, State &)
33{
34 ::exit(0);
35 return true;
36}
37
38} // namespace CLI
39
diff --git a/akonadi2_cli/modules/help/help.h b/akonadi2_cli/modules/help/help.h
deleted file mode 100644
index df1cfc2..0000000
--- a/akonadi2_cli/modules/help/help.h
+++ /dev/null
@@ -1,37 +0,0 @@
1/*
2 * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#pragma once
21
22#include "module.h"
23
24namespace CLI
25{
26
27class Help : public Module
28{
29public:
30 Help();
31
32private:
33 static bool showHelp(const QStringList &commands, State &state);
34};
35
36} // namespace CLI
37
diff --git a/akonadi2_cli/repl/replStates.cpp b/akonadi2_cli/repl/replStates.cpp
index e87dd5f..efa1353 100644
--- a/akonadi2_cli/repl/replStates.cpp
+++ b/akonadi2_cli/repl/replStates.cpp
@@ -107,7 +107,7 @@ void EvalState::onEntry(QEvent *event)
107 if (m_complete) { 107 if (m_complete) {
108 //emit output("Processing ... " + command); 108 //emit output("Processing ... " + command);
109 const QStringList commands = command.split(" "); 109 const QStringList commands = command.split(" ");
110 Module::run(commands); 110 Module::self()->run(commands);
111 emit completed(); 111 emit completed();
112 } 112 }
113 } 113 }
@@ -143,7 +143,7 @@ static char **akonadi2_cli_tab_completion(const char *text, int start, int end)
143 143
144static char *akonadi2_cli_next_tab_complete_match(const char *text, int state) 144static char *akonadi2_cli_next_tab_complete_match(const char *text, int state)
145{ 145{
146 QVector<Module::Syntax> nearest = Module::nearestSyntax(tab_completion_full_state, QString(text)); 146 QVector<Module::Syntax> nearest = Module::self()->nearestSyntax(tab_completion_full_state, QString(text));
147 147
148 if (nearest.size() > state) { 148 if (nearest.size() > state) {
149 return qstrdup(nearest[state].keyword.toUtf8()); 149 return qstrdup(nearest[state].keyword.toUtf8());