summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt3
-rw-r--r--akonadish/CMakeLists.txt28
-rw-r--r--akonadish/TODO9
-rw-r--r--akonadish/akonadish_utils.cpp86
-rw-r--r--akonadish/akonadish_utils.h82
-rw-r--r--akonadish/main.cpp81
-rw-r--r--akonadish/repl/repl.cpp91
-rw-r--r--akonadish/repl/repl.h35
-rw-r--r--akonadish/repl/replStates.cpp171
-rw-r--r--akonadish/repl/replStates.h87
-rw-r--r--akonadish/state.cpp125
-rw-r--r--akonadish/state.h48
-rw-r--r--akonadish/syntax_modules/akonadi_clear.cpp61
-rw-r--r--akonadish/syntax_modules/akonadi_count.cpp81
-rw-r--r--akonadish/syntax_modules/akonadi_create.cpp118
-rw-r--r--akonadish/syntax_modules/akonadi_list.cpp119
-rw-r--r--akonadish/syntax_modules/akonadi_modify.cpp121
-rw-r--r--akonadish/syntax_modules/akonadi_remove.cpp111
-rw-r--r--akonadish/syntax_modules/akonadi_stat.cpp113
-rw-r--r--akonadish/syntax_modules/akonadi_sync.cpp69
-rw-r--r--akonadish/syntax_modules/core_syntax.cpp178
-rw-r--r--akonadish/syntaxtree.cpp216
-rw-r--r--akonadish/syntaxtree.h80
-rw-r--r--cmake/modules/FindReadline.cmake47
24 files changed, 2160 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c480ddd..a827a10 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -57,4 +57,7 @@ add_subdirectory(examples)
57# some tests 57# some tests
58add_subdirectory(tests) 58add_subdirectory(tests)
59 59
60# cli
61add_subdirectory(akonadish)
62
60feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) 63feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
diff --git a/akonadish/CMakeLists.txt b/akonadish/CMakeLists.txt
new file mode 100644
index 0000000..6761a32
--- /dev/null
+++ b/akonadish/CMakeLists.txt
@@ -0,0 +1,28 @@
1project(akonadish)
2
3find_package(Readline REQUIRED)
4
5
6set(akonadi2_cli_SRCS
7 main.cpp
8 syntaxtree.cpp
9 syntax_modules/core_syntax.cpp
10 syntax_modules/akonadi_list.cpp
11 syntax_modules/akonadi_clear.cpp
12 syntax_modules/akonadi_count.cpp
13 syntax_modules/akonadi_create.cpp
14 syntax_modules/akonadi_modify.cpp
15 syntax_modules/akonadi_remove.cpp
16 syntax_modules/akonadi_stat.cpp
17 syntax_modules/akonadi_sync.cpp
18 akonadish_utils.cpp
19 repl/repl.cpp
20 repl/replStates.cpp
21 state.cpp)
22
23include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
24
25add_executable(${PROJECT_NAME} ${akonadi2_cli_SRCS})
26target_link_libraries(${PROJECT_NAME} Qt5::Core ${Readline_LIBRARY} akonadi2common)
27install(TARGETS ${PROJECT_NAME} ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
28
diff --git a/akonadish/TODO b/akonadish/TODO
new file mode 100644
index 0000000..469dbf2
--- /dev/null
+++ b/akonadish/TODO
@@ -0,0 +1,9 @@
1* commands
2 * improve modify/remove/create to dynamically add syntax items for each type
3 * improve modify/remove/create to autocomplete resource names
4* provide a setting to turn on/off user interaction during commands (e.g. deletion confirmations)
5 * add a "ask the user a question" helper in State
6* key/value syntax objects
7* json objects! (set json on; ...)
8* make the shell generic and have it load a plugin matching the name of argv[0] for syntax
9
diff --git a/akonadish/akonadish_utils.cpp b/akonadish/akonadish_utils.cpp
new file mode 100644
index 0000000..ffbdcb3
--- /dev/null
+++ b/akonadish/akonadish_utils.cpp
@@ -0,0 +1,86 @@
1/*
2 * Copyright (C) 2015 Aaron Seigo <aseigo@kolabsystems.com>
3 * Copyright (C) 2015 Christian Mollekopf <mollekopf@kolabsystems.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the
17 * Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21#include "akonadish_utils.h"
22
23#include "common/clientapi.h"
24
25namespace AkonadishUtils
26{
27
28bool isValidStoreType(const QString &type)
29{
30 static const QSet<QString> types = QSet<QString>() << "folder" << "mail" << "event" << "resource";
31 return types.contains(type);
32}
33
34StoreBase &getStore(const QString &type)
35{
36 if (type == "folder") {
37 static Store<Akonadi2::ApplicationDomain::Folder> store;
38 return store;
39 } else if (type == "mail") {
40 static Store<Akonadi2::ApplicationDomain::Mail> store;
41 return store;
42 } else if (type == "event") {
43 static Store<Akonadi2::ApplicationDomain::Event> store;
44 return store;
45 } else if (type == "resource") {
46 static Store<Akonadi2::ApplicationDomain::AkonadiResource> store;
47 return store;
48 }
49
50 //TODO: reinstate the warning+assert
51 //Q_ASSERT(false);
52 //qWarning() << "Trying to get a store that doesn't exist, falling back to event";
53 static Store<Akonadi2::ApplicationDomain::Event> store;
54 return store;
55}
56
57QSharedPointer<QAbstractItemModel> loadModel(const QString &type, Akonadi2::Query query)
58{
59 if (type == "folder") {
60 query.requestedProperties << "name" << "parent";
61 } else if (type == "mail") {
62 query.requestedProperties << "subject" << "folder" << "date";
63 } else if (type == "event") {
64 query.requestedProperties << "summary";
65 } else if (type == "resource") {
66 query.requestedProperties << "type";
67 }
68 auto model = getStore(type).loadModel(query);
69 Q_ASSERT(model);
70 return model;
71}
72
73QMap<QString, QString> keyValueMapFromArgs(const QStringList &args)
74{
75 //TODO: this is not the most clever of algorithms. preserved during the port of commands
76 // from akonadi2_client ... we can probably do better, however ;)
77 QMap<QString, QString> map;
78 for (int i = 0; i + 2 <= args.size(); i += 2) {
79 map.insert(args.at(i), args.at(i + 1));
80 }
81
82 return map;
83}
84
85}
86
diff --git a/akonadish/akonadish_utils.h b/akonadish/akonadish_utils.h
new file mode 100644
index 0000000..17b8ec7
--- /dev/null
+++ b/akonadish/akonadish_utils.h
@@ -0,0 +1,82 @@
1/*
2 * Copyright (C) 2015 Aaron Seigo <aseigo@kolabsystems.com>
3 * Copyright (C) 2015 Christian Mollekopf <mollekopf@kolabsystems.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the
17 * Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21#pragma once
22
23#include <QAbstractItemModel>
24#include <QSharedPointer>
25
26#include "common/query.h"
27#include "common/clientapi.h"
28
29namespace AkonadishUtils
30{
31
32class StoreBase;
33
34bool isValidStoreType(const QString &type);
35StoreBase &getStore(const QString &type);
36QSharedPointer<QAbstractItemModel> loadModel(const QString &type, Akonadi2::Query query);
37QMap<QString, QString> keyValueMapFromArgs(const QStringList &args);
38
39/**
40 * A small abstraction layer to use the akonadi store with the type available as string.
41 */
42class StoreBase {
43public:
44 virtual Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr getObject() = 0;
45 virtual Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr getObject(const QByteArray &resourceInstanceIdentifier, const QByteArray &identifier = QByteArray()) = 0;
46 virtual KAsync::Job<void> create(const Akonadi2::ApplicationDomain::ApplicationDomainType &type) = 0;
47 virtual KAsync::Job<void> modify(const Akonadi2::ApplicationDomain::ApplicationDomainType &type) = 0;
48 virtual KAsync::Job<void> remove(const Akonadi2::ApplicationDomain::ApplicationDomainType &type) = 0;
49 virtual QSharedPointer<QAbstractItemModel> loadModel(const Akonadi2::Query &query) = 0;
50};
51
52template <typename T>
53class Store : public StoreBase {
54public:
55 Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr getObject() Q_DECL_OVERRIDE {
56 return T::Ptr::create();
57 }
58
59 Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr getObject(const QByteArray &resourceInstanceIdentifier, const QByteArray &identifier = QByteArray()) Q_DECL_OVERRIDE {
60 return T::Ptr::create(resourceInstanceIdentifier, identifier, 0, QSharedPointer<Akonadi2::ApplicationDomain::MemoryBufferAdaptor>::create());
61 }
62
63 KAsync::Job<void> create(const Akonadi2::ApplicationDomain::ApplicationDomainType &type) Q_DECL_OVERRIDE {
64 return Akonadi2::Store::create<T>(*static_cast<const T*>(&type));
65 }
66
67 KAsync::Job<void> modify(const Akonadi2::ApplicationDomain::ApplicationDomainType &type) Q_DECL_OVERRIDE {
68 return Akonadi2::Store::modify<T>(*static_cast<const T*>(&type));
69 }
70
71 KAsync::Job<void> remove(const Akonadi2::ApplicationDomain::ApplicationDomainType &type) Q_DECL_OVERRIDE {
72 return Akonadi2::Store::remove<T>(*static_cast<const T*>(&type));
73 }
74
75 QSharedPointer<QAbstractItemModel> loadModel(const Akonadi2::Query &query) Q_DECL_OVERRIDE {
76 return Akonadi2::Store::loadModel<T>(query);
77 }
78};
79
80
81}
82
diff --git a/akonadish/main.cpp b/akonadish/main.cpp
new file mode 100644
index 0000000..bd85fb4
--- /dev/null
+++ b/akonadish/main.cpp
@@ -0,0 +1,81 @@
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 <unistd.h>
21
22#include <QCoreApplication>
23#include <QDebug>
24#include <QTextStream>
25
26#include "syntaxtree.h"
27// #include "jsonlistener.h"
28#include "repl/repl.h"
29
30/*
31 * modes of operation:
32 *
33 * 1. called with no commands: start the REPL and listen for JSON on stin
34 * 2. called with -: listen for JSON on stdin
35 * 3. called with commands: try to match to syntx
36 */
37
38int main(int argc, char *argv[])
39{
40 const bool interactive = isatty(fileno(stdin));
41 const bool startRepl = (argc == 1) && interactive;
42 //TODO: make a json command parse cause that would be awesomesauce
43 const bool startJsonListener = !startRepl &&
44 (argc == 2 && qstrcmp(argv[1], "-") == 0);
45 //qDebug() << "state at startup is" << interactive << startRepl << startJsonListener;
46
47 QCoreApplication app(argc, argv);
48 app.setApplicationName(argv[0]);
49
50 if (startRepl || startJsonListener) {
51 if (startRepl) {
52 Repl *repl = new Repl;
53 QObject::connect(repl, &QStateMachine::finished,
54 repl, &QObject::deleteLater);
55 QObject::connect(repl, &QStateMachine::finished,
56 &app, &QCoreApplication::quit);
57 }
58
59 if (startJsonListener) {
60// JsonListener listener(syntax);
61 }
62
63 State::setHasEventLoop(true);
64 return app.exec();
65 } else if (!interactive) {
66 QTextStream inputStream(stdin);
67 while (true) {
68 const QString input = inputStream.readLine();
69 if (input.isEmpty()) {
70 ::exit(0);
71 }
72
73 const QStringList commands = SyntaxTree::tokenize(input);
74 SyntaxTree::self()->run(commands);
75 }
76 } else {
77 QStringList commands = app.arguments();
78 commands.removeFirst();
79 return SyntaxTree::self()->run(commands);
80 }
81}
diff --git a/akonadish/repl/repl.cpp b/akonadish/repl/repl.cpp
new file mode 100644
index 0000000..499a4af
--- /dev/null
+++ b/akonadish/repl/repl.cpp
@@ -0,0 +1,91 @@
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 "repl.h"
21
22#include <readline/history.h>
23
24#include <QDir>
25#include <QFile>
26#include <QFinalState>
27#include <QStandardPaths>
28#include <QTextStream>
29
30#include "replStates.h"
31#include "syntaxtree.h"
32
33Repl::Repl(QObject *parent)
34 : QStateMachine(parent)
35{
36 // readline history setup
37 using_history();
38 read_history(commandHistoryPath().toLocal8Bit());
39
40 // create all states
41 ReadState *read = new ReadState(this);
42 UnfinishedReadState *unfinishedRead = new UnfinishedReadState(this);
43 EvalState *eval = new EvalState(this);
44 PrintState *print = new PrintState(this);
45 QFinalState *final = new QFinalState(this);
46
47 // connect the transitions
48 read->addTransition(read, SIGNAL(command(QString)), eval);
49 read->addTransition(read, SIGNAL(exitRequested()), final);
50
51 unfinishedRead->addTransition(unfinishedRead, SIGNAL(command(QString)), eval);
52 unfinishedRead->addTransition(unfinishedRead, SIGNAL(exitRequested()), final);
53
54 eval->addTransition(eval, SIGNAL(completed()), read);
55 eval->addTransition(eval, SIGNAL(continueInput()), unfinishedRead);
56 eval->addTransition(eval, SIGNAL(output(QString)), print);
57
58 print->addTransition(print, SIGNAL(completed()), eval);
59
60 setInitialState(read);
61 printWelcomeBanner();
62 start();
63}
64
65Repl::~Repl()
66{
67 // readline history writing
68 write_history(commandHistoryPath().toLocal8Bit());
69}
70
71void Repl::printWelcomeBanner()
72{
73 QTextStream out(stdout);
74 out << QObject::tr("Welcome to the Akonadi2 interative shell!\n");
75 out << QObject::tr("Type `help` for information on the available commands.\n");
76 out.flush();
77}
78
79QString Repl::commandHistoryPath()
80{
81 const QString path = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
82
83 if (!QFile::exists(path)) {
84 QDir dir;
85 dir.mkpath(path);
86 }
87
88 return path + "/repl_history";
89}
90
91#include "moc_repl.cpp"
diff --git a/akonadish/repl/repl.h b/akonadish/repl/repl.h
new file mode 100644
index 0000000..d8d2533
--- /dev/null
+++ b/akonadish/repl/repl.h
@@ -0,0 +1,35 @@
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 <QStateMachine>
23
24class Repl : public QStateMachine
25{
26 Q_OBJECT
27
28public:
29 Repl(QObject *parent = 0);
30 ~Repl();
31
32private:
33 static void printWelcomeBanner();
34 static QString commandHistoryPath();
35};
diff --git a/akonadish/repl/replStates.cpp b/akonadish/repl/replStates.cpp
new file mode 100644
index 0000000..62888d0
--- /dev/null
+++ b/akonadish/repl/replStates.cpp
@@ -0,0 +1,171 @@
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 "replStates.h"
21
22#include <unistd.h>
23#include <iostream>
24
25#include <readline/readline.h>
26#include <readline/history.h>
27
28#include <QDebug>
29#include <QEvent>
30#include <QStateMachine>
31
32#include "syntaxtree.h"
33
34static char *akonadi2_cli_next_tab_complete_match(const char *text, int state);
35static char ** akonadi2_cli_tab_completion(const char *text, int start, int end);
36
37ReadState::ReadState(QState *parent)
38 : QState(parent)
39{
40 rl_completion_entry_function = akonadi2_cli_next_tab_complete_match;
41 rl_attempted_completion_function = akonadi2_cli_tab_completion;
42}
43
44void ReadState::onEntry(QEvent *event)
45{
46 Q_UNUSED(event)
47 char *line = readline(prompt());
48
49 if (!line) {
50 std::cout << std::endl;
51 emit exitRequested();
52 return;
53 }
54
55 // we have actual data, so let's wait for a full line of text
56 QByteArray input(line);
57 const QString text = QString(line).simplified();
58 //qDebug() << "Line is ... " << text;
59
60 if (text.length() > 0) {
61 add_history(line);
62 }
63
64 free(line);
65 emit command(text);
66}
67
68const char *ReadState::prompt() const
69{
70 return "> ";
71}
72
73UnfinishedReadState::UnfinishedReadState(QState *parent)
74 : ReadState(parent)
75{
76}
77
78const char *UnfinishedReadState::prompt() const
79{
80 return " ";
81}
82
83EvalState::EvalState(QState *parent)
84 : QState(parent)
85{
86}
87
88void EvalState::onEntry(QEvent *event)
89{
90 QStateMachine::SignalEvent *e = dynamic_cast<QStateMachine::SignalEvent*>(event);
91
92 const QString command = e ? e->arguments()[0].toString() : QString();
93
94 if (command.isEmpty()) {
95 complete();
96 return;
97 }
98
99 if (command.right(1) == "\\") {
100 m_partial += " " + command.left(command.size() - 1);
101 continueInput();
102 } else {
103 m_partial += " " + command;
104 complete();
105 }
106}
107
108void EvalState::complete()
109{
110 m_partial = m_partial.simplified();
111
112 if (!m_partial.isEmpty()) {
113 //emit output("Processing ... " + command);
114 const QStringList commands = SyntaxTree::tokenize(m_partial);
115 SyntaxTree::self()->run(commands);
116 m_partial.clear();
117 }
118
119 emit completed();
120}
121
122PrintState::PrintState(QState *parent)
123 : QState(parent)
124{
125}
126
127void PrintState::onEntry(QEvent *event)
128{
129 QStateMachine::SignalEvent *e = dynamic_cast<QStateMachine::SignalEvent*>(event);
130
131 if (e && !e->arguments().isEmpty()) {
132 const QString command = e->arguments()[0].toString();
133 QTextStream stream(stdout);
134 stream << command << "\n";
135 }
136
137 emit completed();
138}
139
140static QStringList tab_completion_full_state;
141static bool tab_completion_at_root = false;
142
143static char **akonadi2_cli_tab_completion(const char *text, int start, int end)
144{
145 tab_completion_at_root = start == 0;
146 tab_completion_full_state = QString(rl_line_buffer).remove(start, end - start).split(" ", QString::SkipEmptyParts);
147 return NULL;
148}
149
150static char *akonadi2_cli_next_tab_complete_match(const char *text, int state)
151{
152 const QString fragment(text);
153 Syntax::List nearest = SyntaxTree::self()->nearestSyntax(tab_completion_full_state, fragment);
154 //for (auto syntax: nearest) { qDebug() << "Nearest: " << syntax.keyword; }
155
156 if (nearest.isEmpty()) {
157 SyntaxTree::Command command = SyntaxTree::self()->match(tab_completion_full_state);
158 if (command.first && command.first->completer) {
159 QStringList commandCompletions = command.first->completer(tab_completion_full_state, fragment);
160 if (commandCompletions.size() > state) {
161 return qstrdup(commandCompletions[state].toUtf8());
162 }
163 }
164 } else if (nearest.size() > state) {
165 return qstrdup(nearest[state].keyword.toUtf8());
166 }
167
168 return rl_filename_completion_function(text, state);
169}
170
171#include "moc_replStates.cpp"
diff --git a/akonadish/repl/replStates.h b/akonadish/repl/replStates.h
new file mode 100644
index 0000000..a0d3f90
--- /dev/null
+++ b/akonadish/repl/replStates.h
@@ -0,0 +1,87 @@
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 <QState>
23
24class QSocketNotifier;
25
26class ReadState : public QState
27{
28 Q_OBJECT
29
30public:
31 ReadState(QState *parent = 0);
32
33Q_SIGNALS:
34 void command(const QString &command);
35 void exitRequested();
36
37protected:
38 void onEntry(QEvent *event);
39 virtual const char *prompt() const;
40};
41
42class UnfinishedReadState : public ReadState
43{
44 Q_OBJECT
45
46public:
47 UnfinishedReadState(QState *parent = 0);
48
49protected:
50 const char *prompt() const;
51};
52
53class EvalState : public QState
54{
55 Q_OBJECT
56
57public:
58 EvalState(QState *parent = 0);
59
60Q_SIGNALS:
61 void completed();
62 void continueInput();
63 void output(const QString &output);
64
65protected:
66 void onEntry(QEvent *event);
67
68private:
69 void complete();
70
71 QString m_partial;
72};
73
74class PrintState : public QState
75{
76 Q_OBJECT
77
78public:
79 PrintState(QState *parent = 0);
80
81Q_SIGNALS:
82 void completed();
83
84protected:
85 void onEntry(QEvent *event);
86};
87
diff --git a/akonadish/state.cpp b/akonadish/state.cpp
new file mode 100644
index 0000000..f3f5975
--- /dev/null
+++ b/akonadish/state.cpp
@@ -0,0 +1,125 @@
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 "state.h"
21
22#include <QCoreApplication>
23#include <QDebug>
24#include <QEventLoop>
25#include <QTextStream>
26
27static bool s_hasEventLoop = false;
28
29class State::Private
30{
31public:
32 Private()
33 : outStream(stdout)
34 {
35 }
36
37 QEventLoop *eventLoop()
38 {
39 if (!event) {
40 event = new QEventLoop;
41 }
42
43 return event;
44 }
45
46 int debugLevel = 0;
47 QEventLoop *event = 0;
48 bool timing = false;
49 QTextStream outStream;
50};
51
52State::State()
53 : d(new Private)
54{
55}
56
57void State::print(const QString &message, unsigned int indentationLevel) const
58{
59 for (unsigned int i = 0; i < indentationLevel; ++i) {
60 d->outStream << "\t";
61 }
62
63 d->outStream << message;
64}
65
66void State::printLine(const QString &message, unsigned int indentationLevel) const
67{
68 print(message, indentationLevel);
69 d->outStream << "\n";
70 d->outStream.flush();
71}
72
73void State::printError(const QString &errorMessage, const QString &errorCode) const
74{
75 printLine("ERROR" + (errorCode.isEmpty() ? "" : " " + errorCode) + ": " + errorMessage);
76}
77
78void State::setDebugLevel(unsigned int level)
79{
80 if (level < 7) {
81 d->debugLevel = level;
82 }
83}
84
85unsigned int State::debugLevel() const
86{
87 return d->debugLevel;
88}
89
90int State::commandStarted() const
91{
92 if (!s_hasEventLoop) {
93 return QCoreApplication::exec();
94 } else if (!d->eventLoop()->isRunning()) {
95 return d->eventLoop()->exec();
96 }
97
98 return 0;
99}
100
101void State::commandFinished(int returnCode) const
102{
103 if (!s_hasEventLoop) {
104 QCoreApplication::exit(returnCode);
105 } else {
106 d->eventLoop()->exit(returnCode);
107 }
108}
109
110void State::setHasEventLoop(bool evented)
111{
112 s_hasEventLoop = evented;
113}
114
115void State::setCommandTiming(bool time)
116{
117 d->timing = time;
118}
119
120bool State::commandTiming() const
121{
122 return d->timing;
123}
124
125
diff --git a/akonadish/state.h b/akonadish/state.h
new file mode 100644
index 0000000..9c1ab6f
--- /dev/null
+++ b/akonadish/state.h
@@ -0,0 +1,48 @@
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 <QTextStream>
23
24class State
25{
26public:
27 State();
28
29 void print(const QString &message, unsigned int indentationLevel = 0) const;
30 void printLine(const QString &message = QString(), unsigned int indentationLevel = 0) const;
31 void printError(const QString &errorMessage, const QString &errorCode = QString()) const;
32
33 void setDebugLevel(unsigned int level);
34 unsigned int debugLevel() const;
35
36 void setCommandTiming(bool);
37 bool commandTiming() const;
38
39 int commandStarted() const;
40 void commandFinished(int returnCode = 0) const;
41
42 static void setHasEventLoop(bool evented);
43
44private:
45 class Private;
46 Private * const d;
47};
48
diff --git a/akonadish/syntax_modules/akonadi_clear.cpp b/akonadish/syntax_modules/akonadi_clear.cpp
new file mode 100644
index 0000000..d17fac2
--- /dev/null
+++ b/akonadish/syntax_modules/akonadi_clear.cpp
@@ -0,0 +1,61 @@
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 <QDebug>
21#include <QObject> // tr()
22#include <QTimer>
23
24#include "common/resource.h"
25#include "common/storage.h"
26#include "common/domain/event.h"
27#include "common/domain/folder.h"
28#include "common/resourceconfig.h"
29#include "common/log.h"
30#include "common/storage.h"
31#include "common/definitions.h"
32
33#include "akonadish_utils.h"
34#include "state.h"
35#include "syntaxtree.h"
36
37namespace AkonadiClear
38{
39
40bool clear(const QStringList &args, State &state)
41{
42 for (const auto &resource : args) {
43 state.print(QObject::tr("Removing local cache for '%1' ...").arg(resource));
44 Akonadi2::Store::removeFromDisk(resource.toLatin1());
45 state.printLine(QObject::tr("done"));
46 }
47
48 return true;
49}
50
51Syntax::List syntax()
52{
53 Syntax::List syntax;
54 syntax << Syntax("clear", QObject::tr("Clears the local cache of one or more resources (be careful!)"), &AkonadiClear::clear);
55
56 return syntax;
57}
58
59REGISTER_SYNTAX(AkonadiClear)
60
61}
diff --git a/akonadish/syntax_modules/akonadi_count.cpp b/akonadish/syntax_modules/akonadi_count.cpp
new file mode 100644
index 0000000..70aabc9
--- /dev/null
+++ b/akonadish/syntax_modules/akonadi_count.cpp
@@ -0,0 +1,81 @@
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 <QCoreApplication>
21#include <QDebug>
22#include <QObject> // tr()
23#include <QModelIndex>
24#include <QTime>
25
26#include "common/resource.h"
27#include "common/storage.h"
28#include "common/domain/event.h"
29#include "common/domain/folder.h"
30#include "common/resourceconfig.h"
31#include "common/log.h"
32#include "common/storage.h"
33#include "common/definitions.h"
34
35#include "akonadish_utils.h"
36#include "state.h"
37#include "syntaxtree.h"
38
39namespace AkonadiCount
40{
41
42bool count(const QStringList &args, State &state)
43{
44 auto resources = args;
45 auto type = !resources.isEmpty() ? resources.takeFirst() : QString();
46
47 if (!type.isEmpty() && !AkonadishUtils::isValidStoreType(type)) {
48 state.printError(QObject::tr("Unknown type: %1").arg(type));
49 return false;
50 }
51
52 Akonadi2::Query query;
53 for (const auto &res : resources) {
54 query.resources << res.toLatin1();
55 }
56 query.syncOnDemand = false;
57 query.processAll = false;
58 query.liveQuery = false;
59
60 auto model = AkonadishUtils::loadModel(type, query);
61 QObject::connect(model.data(), &QAbstractItemModel::dataChanged, [model, state](const QModelIndex &, const QModelIndex &, const QVector<int> &roles) {
62 if (roles.contains(Akonadi2::Store::ChildrenFetchedRole)) {
63 state.printLine(QObject::tr("Counted results %1").arg(model->rowCount(QModelIndex())));
64 state.commandFinished();
65 }
66 });
67
68 return true;
69}
70
71Syntax::List syntax()
72{
73 Syntax::List syntax;
74 syntax << Syntax("count", QObject::tr("Returns the number of items of a given type in a resource. Usage: count <type> <resource>"), &AkonadiCount::count, Syntax::EventDriven);
75
76 return syntax;
77}
78
79REGISTER_SYNTAX(AkonadiCount)
80
81}
diff --git a/akonadish/syntax_modules/akonadi_create.cpp b/akonadish/syntax_modules/akonadi_create.cpp
new file mode 100644
index 0000000..377219a
--- /dev/null
+++ b/akonadish/syntax_modules/akonadi_create.cpp
@@ -0,0 +1,118 @@
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 <QCoreApplication>
21#include <QDebug>
22#include <QObject> // tr()
23#include <QModelIndex>
24#include <QTime>
25
26#include "common/resource.h"
27#include "common/storage.h"
28#include "common/domain/event.h"
29#include "common/domain/folder.h"
30#include "common/resourceconfig.h"
31#include "common/log.h"
32#include "common/storage.h"
33#include "common/definitions.h"
34
35#include "akonadish_utils.h"
36#include "state.h"
37#include "syntaxtree.h"
38
39namespace AkonadiCreate
40{
41
42bool create(const QStringList &allArgs, State &state)
43{
44 if (allArgs.isEmpty()) {
45 state.printError(QObject::tr("A type is required"), "akonadicreate/02");
46 return false;
47 }
48
49 if (allArgs.count() < 2) {
50 state.printError(QObject::tr("A resource ID is required to create items"), "akonadicreate/03");
51 return false;
52 }
53
54 auto args = allArgs;
55 auto type = args.takeFirst();
56 auto &store = AkonadishUtils::getStore(type);
57 Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr object;
58 auto resource = args.takeFirst().toLatin1();
59 object = store.getObject(resource);
60
61 auto map = AkonadishUtils::keyValueMapFromArgs(args);
62 for (auto i = map.begin(); i != map.end(); ++i) {
63 object->setProperty(i.key().toLatin1(), i.value());
64 }
65
66 auto result = store.create(*object).exec();
67 result.waitForFinished();
68 if (result.errorCode()) {
69 state.printError(QObject::tr("An error occurred while creating the entity: %1").arg(result.errorMessage()),
70 "akonaid_create_e" + QString::number(result.errorCode()));
71 }
72
73 return true;
74}
75
76bool resource(const QStringList &args, State &state)
77{
78 if (args.isEmpty()) {
79 state.printError(QObject::tr("A resource can not be created without a type"), "akonadicreate/01");
80 return false;
81 }
82
83 auto &store = AkonadishUtils::getStore("resource");
84
85 auto resourceType = args.at(0);
86 Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr object = store.getObject("");
87 object->setProperty("type", resourceType);
88
89 auto map = AkonadishUtils::keyValueMapFromArgs(args);
90 for (auto i = map.begin(); i != map.end(); ++i) {
91 object->setProperty(i.key().toLatin1(), i.value());
92 }
93
94 auto result = store.create(*object).exec();
95 result.waitForFinished();
96 if (result.errorCode()) {
97 state.printError(QObject::tr("An error occurred while creating the entity: %1").arg(result.errorMessage()),
98 "akonaid_create_e" + QString::number(result.errorCode()));
99 }
100
101 return true;
102}
103
104
105Syntax::List syntax()
106{
107 Syntax::List syntax;
108
109 Syntax create("create", QObject::tr("Create items in a resource"), &AkonadiCreate::create);
110 create.children << Syntax("resource", QObject::tr("Creates a new resource"), &AkonadiCreate::resource);
111
112 syntax << create;
113 return syntax;
114}
115
116REGISTER_SYNTAX(AkonadiCreate)
117
118}
diff --git a/akonadish/syntax_modules/akonadi_list.cpp b/akonadish/syntax_modules/akonadi_list.cpp
new file mode 100644
index 0000000..807119c
--- /dev/null
+++ b/akonadish/syntax_modules/akonadi_list.cpp
@@ -0,0 +1,119 @@
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 <QCoreApplication>
21#include <QDebug>
22#include <QObject> // tr()
23#include <QModelIndex>
24#include <QTime>
25
26#include "common/resource.h"
27#include "common/storage.h"
28#include "common/domain/event.h"
29#include "common/domain/folder.h"
30#include "common/resourceconfig.h"
31#include "common/log.h"
32#include "common/storage.h"
33#include "common/definitions.h"
34
35#include "akonadish_utils.h"
36#include "state.h"
37#include "syntaxtree.h"
38
39namespace AkonadiList
40{
41
42bool list(const QStringList &args, State &state)
43{
44 if (args.isEmpty()) {
45 state.printError(QObject::tr("Please provide at least one type to list (e.g. resource, .."));
46 return false;
47 }
48
49 auto resources = args;
50 auto type = !resources.isEmpty() ? resources.takeFirst() : QString();
51
52 if (!type.isEmpty() && !AkonadishUtils::isValidStoreType(type)) {
53 state.printError(QObject::tr("Unknown type: %1").arg(type));
54 return false;
55 }
56
57 Akonadi2::Query query;
58 for (const auto &res : resources) {
59 query.resources << res.toLatin1();
60 }
61 query.syncOnDemand = false;
62 query.processAll = false;
63 query.liveQuery = false;
64
65 QTime time;
66 time.start();
67 auto model = AkonadishUtils::loadModel(type, query);
68 if (state.debugLevel() > 0) {
69 state.printLine(QObject::tr("Folder type %1").arg(type));
70 state.printLine(QObject::tr("Loaded model in %1 ms").arg(time.elapsed()));
71 }
72
73 //qDebug() << "Listing";
74 int colSize = 38; //Necessary to display a complete UUID
75 state.print(" " + QObject::tr("Column") + " ");
76 state.print(QObject::tr("Resource").leftJustified(colSize, ' ', true) +
77 QObject::tr("Identifier").leftJustified(colSize, ' ', true));
78 for (int i = 0; i < model->columnCount(QModelIndex()); i++) {
79 state.print(" | " + model->headerData(i, Qt::Horizontal).toString().leftJustified(colSize, ' ', true));
80 }
81 state.printLine();
82
83 QObject::connect(model.data(), &QAbstractItemModel::rowsInserted, [model, colSize, state](const QModelIndex &index, int start, int end) {
84 for (int i = start; i <= end; i++) {
85 state.print(" " + QObject::tr("Row %1").arg(QString::number(model->rowCount())).rightJustified(4, ' ') + ": ");
86 auto object = model->data(model->index(i, 0, index), Akonadi2::Store::DomainObjectBaseRole).value<Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr>();
87 state.print(" " + object->resourceInstanceIdentifier().leftJustified(colSize, ' ', true));
88 state.print(object->identifier().leftJustified(colSize, ' ', true));
89 for (int col = 0; col < model->columnCount(QModelIndex()); col++) {
90 state.print(" | " + model->data(model->index(i, col, index)).toString().leftJustified(colSize, ' ', true));
91 }
92 state.printLine();
93 }
94 });
95
96 QObject::connect(model.data(), &QAbstractItemModel::dataChanged, [model, state](const QModelIndex &, const QModelIndex &, const QVector<int> &roles) {
97 if (roles.contains(Akonadi2::Store::ChildrenFetchedRole)) {
98 state.commandFinished();
99 }
100 });
101
102 if (!model->data(QModelIndex(), Akonadi2::Store::ChildrenFetchedRole).toBool()) {
103 return true;
104 }
105
106 return false;
107}
108
109Syntax::List syntax()
110{
111 Syntax::List syntax;
112 syntax << Syntax("list", QObject::tr("List all resources, or the contents of one or more resources"), &AkonadiList::list, Syntax::EventDriven);
113
114 return syntax;
115}
116
117REGISTER_SYNTAX(AkonadiList)
118
119}
diff --git a/akonadish/syntax_modules/akonadi_modify.cpp b/akonadish/syntax_modules/akonadi_modify.cpp
new file mode 100644
index 0000000..8438301
--- /dev/null
+++ b/akonadish/syntax_modules/akonadi_modify.cpp
@@ -0,0 +1,121 @@
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 <QCoreApplication>
21#include <QDebug>
22#include <QObject> // tr()
23#include <QModelIndex>
24#include <QTime>
25
26#include "common/resource.h"
27#include "common/storage.h"
28#include "common/domain/event.h"
29#include "common/domain/folder.h"
30#include "common/resourceconfig.h"
31#include "common/log.h"
32#include "common/storage.h"
33#include "common/definitions.h"
34
35#include "akonadish_utils.h"
36#include "state.h"
37#include "syntaxtree.h"
38
39namespace AkonadiModify
40{
41
42bool modify(const QStringList &args, State &state)
43{
44 if (args.isEmpty()) {
45 state.printError(QObject::tr("A type is required"), "akonadi_modify/02");
46 return false;
47 }
48
49 if (args.count() < 2) {
50 state.printError(QObject::tr("A resource ID is required to remove items"), "akonadi_modify/03");
51 return false;
52 }
53
54 if (args.count() < 3) {
55 state.printError(QObject::tr("An object ID is required to remove items"), "akonadi_modify/03");
56 return false;
57 }
58
59 auto type = args[0];
60 auto resourceId = args[1];
61 auto identifier = args[2];
62
63 auto &store = AkonadishUtils::getStore(type);
64 Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr object = store.getObject(resourceId.toUtf8(), identifier.toUtf8());
65
66 auto map = AkonadishUtils::keyValueMapFromArgs(args);
67 for (auto i = map.begin(); i != map.end(); ++i) {
68 object->setProperty(i.key().toLatin1(), i.value());
69 }
70
71 auto result = store.modify(*object).exec();
72 result.waitForFinished();
73 if (result.errorCode()) {
74 state.printError(QObject::tr("An error occurred while removing %1 from %1: %2").arg(identifier).arg(resourceId).arg(result.errorMessage()),
75 "akonaid__modify_e" + QString::number(result.errorCode()));
76 }
77
78 return true;
79}
80
81bool resource(const QStringList &args, State &state)
82{
83 if (args.isEmpty()) {
84 state.printError(QObject::tr("A resource can not be modified without an id"), "akonadi_modify/01");
85 }
86
87 auto &store = AkonadishUtils::getStore("resource");
88
89 auto resourceId = args.at(0);
90 Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr object = store.getObject("", resourceId.toLatin1());
91
92 auto map = AkonadishUtils::keyValueMapFromArgs(args);
93 for (auto i = map.begin(); i != map.end(); ++i) {
94 object->setProperty(i.key().toLatin1(), i.value());
95 }
96
97 auto result = store.modify(*object).exec();
98 result.waitForFinished();
99 if (result.errorCode()) {
100 state.printError(QObject::tr("An error occurred while modifying the resource %1: %2").arg(resourceId).arg(result.errorMessage()),
101 "akonaid_modify_e" + QString::number(result.errorCode()));
102 }
103
104 return true;
105}
106
107
108Syntax::List syntax()
109{
110 Syntax::List syntax;
111
112 Syntax modify("modify", QObject::tr("Modify items in a resource"), &AkonadiModify::modify);
113 modify.children << Syntax("resource", QObject::tr("Modify a resource"), &AkonadiModify::resource);//, Syntax::EventDriven);
114
115 syntax << modify;
116 return syntax;
117}
118
119REGISTER_SYNTAX(AkonadiModify)
120
121}
diff --git a/akonadish/syntax_modules/akonadi_remove.cpp b/akonadish/syntax_modules/akonadi_remove.cpp
new file mode 100644
index 0000000..bf09e2e
--- /dev/null
+++ b/akonadish/syntax_modules/akonadi_remove.cpp
@@ -0,0 +1,111 @@
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 <QCoreApplication>
21#include <QDebug>
22#include <QObject> // tr()
23#include <QModelIndex>
24#include <QTime>
25
26#include "common/resource.h"
27#include "common/storage.h"
28#include "common/domain/event.h"
29#include "common/domain/folder.h"
30#include "common/resourceconfig.h"
31#include "common/log.h"
32#include "common/storage.h"
33#include "common/definitions.h"
34
35#include "akonadish_utils.h"
36#include "state.h"
37#include "syntaxtree.h"
38
39namespace AkonadiRemove
40{
41
42bool remove(const QStringList &args, State &state)
43{
44 if (args.isEmpty()) {
45 state.printError(QObject::tr("A type is required"), "akonadi_remove/02");
46 return false;
47 }
48
49 if (args.count() < 2) {
50 state.printError(QObject::tr("A resource ID is required to remove items"), "akonadi_remove/03");
51 return false;
52 }
53
54 if (args.count() < 3) {
55 state.printError(QObject::tr("An object ID is required to remove items"), "akonadi_remove/03");
56 return false;
57 }
58
59 auto type = args[0];
60 auto resourceId = args[1];
61 auto identifier = args[2];
62
63 auto &store = AkonadishUtils::getStore(type);
64 Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr object = store.getObject(resourceId.toUtf8(), identifier.toUtf8());
65
66 auto result = store.remove(*object).exec();
67 result.waitForFinished();
68 if (result.errorCode()) {
69 state.printError(QObject::tr("An error occurred while removing %1 from %1: %2").arg(identifier).arg(resourceId).arg(result.errorMessage()),
70 "akonaid_remove_e" + QString::number(result.errorCode()));
71 }
72
73 return true;
74}
75
76bool resource(const QStringList &args, State &state)
77{
78 if (args.isEmpty()) {
79 state.printError(QObject::tr("A resource can not be removed without an id"), "akonadi_remove/01");
80 }
81
82 auto &store = AkonadishUtils::getStore("resource");
83
84 auto resourceId = args.at(0);
85 Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr object = store.getObject("", resourceId.toLatin1());
86
87 auto result = store.remove(*object).exec();
88 result.waitForFinished();
89 if (result.errorCode()) {
90 state.printError(QObject::tr("An error occurred while removing the resource %1: %2").arg(resourceId).arg(result.errorMessage()),
91 "akonaid_remove_e" + QString::number(result.errorCode()));
92 }
93
94 return true;
95}
96
97
98Syntax::List syntax()
99{
100 Syntax::List syntax;
101
102 Syntax remove("remove", QObject::tr("Remove items in a resource"), &AkonadiRemove::remove);
103 remove.children << Syntax("resource", QObject::tr("Removes a resource"), &AkonadiRemove::resource);//, Syntax::EventDriven);
104
105 syntax << remove;
106 return syntax;
107}
108
109REGISTER_SYNTAX(AkonadiRemove)
110
111}
diff --git a/akonadish/syntax_modules/akonadi_stat.cpp b/akonadish/syntax_modules/akonadi_stat.cpp
new file mode 100644
index 0000000..149ccbd
--- /dev/null
+++ b/akonadish/syntax_modules/akonadi_stat.cpp
@@ -0,0 +1,113 @@
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 <QDebug>
21#include <QObject> // tr()
22#include <QTimer>
23
24#include "common/resource.h"
25#include "common/storage.h"
26#include "common/domain/event.h"
27#include "common/domain/folder.h"
28#include "common/resourceconfig.h"
29#include "common/log.h"
30#include "common/storage.h"
31#include "common/definitions.h"
32
33#include "akonadish_utils.h"
34#include "state.h"
35#include "syntaxtree.h"
36
37namespace AkonadiStat
38{
39
40void statResources(const QStringList &resources, const State &state)
41{
42 qint64 total = 0;
43 for (const auto &resource : resources) {
44 Akonadi2::Storage storage(Akonadi2::storageLocation(), resource, Akonadi2::Storage::ReadOnly);
45 auto transaction = storage.createTransaction(Akonadi2::Storage::ReadOnly);
46
47 QList<QByteArray> databases = transaction.getDatabaseNames();
48 for (const auto &databaseName : databases) {
49 state.printLine(QObject::tr("Database: %1").arg(QString(databaseName)), 1);
50 auto db = transaction.openDatabase(databaseName);
51 qint64 size = db.getSize() / 1024;
52 state.printLine(QObject::tr("Size [kb]: %1").arg(size), 1);
53 total += size;
54 }
55 }
56
57 state.printLine(QObject::tr("Total [kb]: %1").arg(total));
58}
59
60bool statAllResources(State &state)
61{
62 Akonadi2::Query query;
63 query.syncOnDemand = false;
64 query.processAll = false;
65 query.liveQuery = false;
66 auto model = AkonadishUtils::loadModel("resource", query);
67
68 //SUUUPER ugly, but can't think of a better way with 2 glasses of wine in me on Christmas day
69 static QStringList resources;
70 resources.clear();
71
72 QObject::connect(model.data(), &QAbstractItemModel::rowsInserted, [model](const QModelIndex &index, int start, int end) mutable {
73 for (int i = start; i <= end; i++) {
74 auto object = model->data(model->index(i, 0, index), Akonadi2::Store::DomainObjectBaseRole).value<Akonadi2::ApplicationDomain::ApplicationDomainType::Ptr>();
75 resources << object->identifier();
76 }
77 });
78
79 QObject::connect(model.data(), &QAbstractItemModel::dataChanged, [model, state](const QModelIndex &, const QModelIndex &, const QVector<int> &roles) {
80 if (roles.contains(Akonadi2::Store::ChildrenFetchedRole)) {
81 statResources(resources, state);
82 state.commandFinished();
83 }
84 });
85
86 if (!model->data(QModelIndex(), Akonadi2::Store::ChildrenFetchedRole).toBool()) {
87 return true;
88 }
89
90 return false;
91}
92
93bool stat(const QStringList &args, State &state)
94{
95 if (args.isEmpty()) {
96 return statAllResources(state);
97 }
98
99 statResources(args, state);
100 return false;
101}
102
103Syntax::List syntax()
104{
105 Syntax::List syntax;
106 syntax << Syntax("stat", QObject::tr("Shows database usage for the resources requested"), &AkonadiStat::stat, Syntax::EventDriven);
107
108 return syntax;
109}
110
111REGISTER_SYNTAX(AkonadiStat)
112
113}
diff --git a/akonadish/syntax_modules/akonadi_sync.cpp b/akonadish/syntax_modules/akonadi_sync.cpp
new file mode 100644
index 0000000..1cf097d
--- /dev/null
+++ b/akonadish/syntax_modules/akonadi_sync.cpp
@@ -0,0 +1,69 @@
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 <QDebug>
21#include <QObject> // tr()
22#include <QTimer>
23
24#include "common/resource.h"
25#include "common/storage.h"
26#include "common/domain/event.h"
27#include "common/domain/folder.h"
28#include "common/resourceconfig.h"
29#include "common/log.h"
30#include "common/storage.h"
31#include "common/definitions.h"
32
33#include "akonadish_utils.h"
34#include "state.h"
35#include "syntaxtree.h"
36
37namespace AkonadiSync
38{
39
40bool sync(const QStringList &args, State &state)
41{
42 Akonadi2::Query query;
43 for (const auto &res : args) {
44 query.resources << res.toLatin1();
45 }
46 query.syncOnDemand = true;
47 query.processAll = true;
48
49 QTimer::singleShot(0, [query, state]() {
50 Akonadi2::Store::synchronize(query).then<void>([state]() {
51 state.printLine("Synchronization complete!");
52 state.commandFinished();
53 }).exec();
54 });
55
56 return true;
57}
58
59Syntax::List syntax()
60{
61 Syntax::List syntax;
62 syntax << Syntax("sync", QObject::tr("Syncronizes all resources that are listed; and empty list triggers a syncronizaton on all resources"), &AkonadiSync::sync, Syntax::EventDriven );
63
64 return syntax;
65}
66
67REGISTER_SYNTAX(AkonadiSync)
68
69}
diff --git a/akonadish/syntax_modules/core_syntax.cpp b/akonadish/syntax_modules/core_syntax.cpp
new file mode 100644
index 0000000..31b824a
--- /dev/null
+++ b/akonadish/syntax_modules/core_syntax.cpp
@@ -0,0 +1,178 @@
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 <QDebug>
21#include <QObject> // tr()
22#include <QSet>
23#include <QTextStream>
24
25#include "state.h"
26#include "syntaxtree.h"
27
28namespace CoreSyntax
29{
30
31bool exit(const QStringList &, State &)
32{
33 ::exit(0);
34 return true;
35}
36
37bool showHelp(const QStringList &commands, State &state)
38{
39 SyntaxTree::Command command = SyntaxTree::self()->match(commands);
40 if (commands.isEmpty()) {
41 state.printLine(QObject::tr("Welcome to the Akonadi2 command line tool!"));
42 state.printLine(QObject::tr("Top-level commands:"));
43
44 QSet<QString> sorted;
45 for (auto syntax: SyntaxTree::self()->syntax()) {
46 sorted.insert(syntax.keyword);
47 }
48
49 for (auto keyword: sorted) {
50 state.printLine(keyword, 1);
51 }
52 } else if (const Syntax *syntax = command.first) {
53 //TODO: get parent!
54 state.print(QObject::tr("Command `%1`").arg(syntax->keyword));
55
56 if (!syntax->help.isEmpty()) {
57 state.print(": " + syntax->help);
58 }
59 state.printLine();
60
61 if (!syntax->children.isEmpty()) {
62 state.printLine("Sub-commands:", 1);
63 QSet<QString> sorted;
64 for (auto childSyntax: syntax->children) {
65 sorted.insert(childSyntax.keyword);
66 }
67
68 for (auto keyword: sorted) {
69 state.printLine(keyword, 1);
70 }
71 }
72 } else {
73 state.printError("Unknown command: " + commands.join(" "));
74 }
75
76 return true;
77}
78
79QStringList showHelpCompleter(const QStringList &commands, const QString &fragment)
80{
81 QStringList items;
82
83 for (auto syntax: SyntaxTree::self()->syntax()) {
84 if (syntax.keyword != QObject::tr("help") &&
85 (fragment.isEmpty() || syntax.keyword.startsWith(fragment))) {
86 items << syntax.keyword;
87 }
88 }
89
90 qSort(items);
91 return items;
92}
93
94bool setDebugLevel(const QStringList &commands, State &state)
95{
96 if (commands.count() != 1) {
97 state.printError(QObject::tr("Wrong number of arguments; expected 1 got %1").arg(commands.count()));
98 return false;
99 }
100
101 bool ok = false;
102 int level = commands[0].toUInt(&ok);
103
104 if (!ok) {
105 state.printError(QObject::tr("Expected a number between 0 and 6, got %1").arg(commands[0]));
106 return false;
107 }
108
109 state.setDebugLevel(level);
110 return true;
111}
112
113bool printDebugLevel(const QStringList &, State &state)
114{
115 state.printLine(QString::number(state.debugLevel()));
116 return true;
117}
118
119bool printCommandTiming(const QStringList &, State &state)
120{
121 state.printLine(state.commandTiming() ? QObject::tr("on") : QObject::tr("off"));
122 return true;
123}
124
125void printSyntaxBranch(State &state, const Syntax::List &list, int depth)
126{
127 if (list.isEmpty()) {
128 return;
129 }
130
131 if (depth > 0) {
132 state.printLine("\\", depth);
133 }
134
135 for (auto syntax: list) {
136 state.print("|-", depth);
137 state.printLine(syntax.keyword);
138 printSyntaxBranch(state, syntax.children, depth + 1);
139 }
140}
141
142bool printSyntaxTree(const QStringList &, State &state)
143{
144 printSyntaxBranch(state, SyntaxTree::self()->syntax(), 0);
145 return true;
146}
147
148Syntax::List syntax()
149{
150 Syntax::List syntax;
151 syntax << Syntax("exit", QObject::tr("Exits the application. Ctrl-d also works!"), &CoreSyntax::exit);
152
153 Syntax help("help", QObject::tr("Print command information: help [command]"), &CoreSyntax::showHelp);
154 help.completer = &CoreSyntax::showHelpCompleter;
155 syntax << help;
156
157 syntax << Syntax("syntaxtree", QString(), &printSyntaxTree);
158
159 Syntax set("set", QObject::tr("Sets settings for the session"));
160 set.children << Syntax("debug", QObject::tr("Set the debug level from 0 to 6"), &CoreSyntax::setDebugLevel);
161 Syntax setTiming = Syntax("timing", QObject::tr("Whether or not to print the time commands take to complete"));
162 setTiming.children << Syntax("on", QString(), [](const QStringList &, State &state) -> bool { state.setCommandTiming(true); return true; });
163 setTiming.children << Syntax("off", QString(), [](const QStringList &, State &state) -> bool { state.setCommandTiming(false); return true; });
164 set.children << setTiming;
165 syntax << set;
166
167 Syntax get("get", QObject::tr("Gets settings for the session"));
168 get.children << Syntax("debug", QObject::tr("The current debug level from 0 to 6"), &CoreSyntax::printDebugLevel);
169 get.children << Syntax("timing", QObject::tr("Whether or not to print the time commands take to complete"), &CoreSyntax::printCommandTiming);
170 syntax << get;
171
172 return syntax;
173}
174
175REGISTER_SYNTAX(CoreSyntax)
176
177} // namespace CoreSyntax
178
diff --git a/akonadish/syntaxtree.cpp b/akonadish/syntaxtree.cpp
new file mode 100644
index 0000000..495ad22
--- /dev/null
+++ b/akonadish/syntaxtree.cpp
@@ -0,0 +1,216 @@
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 "syntaxtree.h"
21
22#include <QCoreApplication>
23#include <QDebug>
24
25SyntaxTree *SyntaxTree::s_module = 0;
26
27Syntax::Syntax()
28{
29}
30
31Syntax::Syntax(const QString &k, const QString &helpText, std::function<bool(const QStringList &, State &)> l, Interactivity inter)
32 : keyword(k),
33 help(helpText),
34 interactivity(inter),
35 lambda(l)
36{
37}
38
39SyntaxTree::SyntaxTree()
40{
41}
42
43int SyntaxTree::registerSyntax(std::function<Syntax::List()> f)
44{
45 m_syntax += f();
46 return m_syntax.size();
47}
48
49SyntaxTree *SyntaxTree::self()
50{
51 if (!s_module) {
52 s_module = new SyntaxTree;
53 }
54
55 return s_module;
56}
57
58Syntax::List SyntaxTree::syntax() const
59{
60 return m_syntax;
61}
62
63bool SyntaxTree::run(const QStringList &commands)
64{
65 bool success = false;
66 m_timeElapsed.start();
67 Command command = match(commands);
68 if (command.first) {
69 if (command.first->lambda) {
70 success = command.first->lambda(command.second, m_state);
71 if (success && command.first->interactivity == Syntax::EventDriven) {
72 success = m_state.commandStarted();
73 }
74 } else if (command.first->children.isEmpty()) {
75 m_state.printError(QObject::tr("Broken command... sorry :("), "st_broken");
76 } else {
77 QStringList keywordList;
78 for (auto syntax: command.first->children) {
79 keywordList << syntax.keyword;
80 }
81 const QString keywords = keywordList.join(" " );
82 m_state.printError(QObject::tr("Command requires additional arguments, one of: %1").arg(keywords));
83 }
84 } else {
85 m_state.printError(QObject::tr("Unknown command"), "st_unknown");
86 }
87
88 if (m_state.commandTiming()) {
89 m_state.printLine(QObject::tr("Time elapsed: %1").arg(m_timeElapsed.elapsed()));
90 }
91 return false;
92}
93
94SyntaxTree::Command SyntaxTree::match(const QStringList &commandLine) const
95{
96 if (commandLine.isEmpty()) {
97 return Command();
98 }
99
100 QStringListIterator commandLineIt(commandLine);
101
102 QVectorIterator<Syntax> syntaxIt(m_syntax);
103 const Syntax *lastFullSyntax = 0;
104 QStringList tailCommands;
105 while (commandLineIt.hasNext() && syntaxIt.hasNext()) {
106 const QString word = commandLineIt.next();
107 while (syntaxIt.hasNext()) {
108 const Syntax &syntax = syntaxIt.next();
109 if (word == syntax.keyword) {
110 lastFullSyntax = &syntax;
111 syntaxIt = syntax.children;
112 break;
113 }
114 }
115 }
116
117 if (lastFullSyntax) {
118 while (commandLineIt.hasNext()) {
119 tailCommands << commandLineIt.next();
120 }
121
122 return std::make_pair(lastFullSyntax, tailCommands);
123 }
124
125 return Command();
126}
127
128Syntax::List SyntaxTree::nearestSyntax(const QStringList &words, const QString &fragment) const
129{
130 Syntax::List matches;
131
132 //qDebug() << "words are" << words;
133 if (words.isEmpty()) {
134 for (const Syntax &syntax: m_syntax) {
135 if (syntax.keyword.startsWith(fragment)) {
136 matches.push_back(syntax);
137 }
138 }
139 } else {
140 QStringListIterator wordIt(words);
141 QVectorIterator<Syntax> syntaxIt(m_syntax);
142 Syntax lastFullSyntax;
143
144 while (wordIt.hasNext()) {
145 const QString &word = wordIt.next();
146 while (syntaxIt.hasNext()) {
147 const Syntax &syntax = syntaxIt.next();
148 if (word == syntax.keyword) {
149 lastFullSyntax = syntax;
150 syntaxIt = syntax.children;
151 break;
152 }
153 }
154 }
155
156 //qDebug() << "exiting with" << lastFullSyntax.keyword << words.last();
157 if (lastFullSyntax.keyword == words.last()) {
158 syntaxIt = lastFullSyntax.children;
159 while (syntaxIt.hasNext()) {
160 Syntax syntax = syntaxIt.next();
161 if (fragment.isEmpty() || syntax.keyword.startsWith(fragment)) {
162 matches.push_back(syntax);
163 }
164 }
165 }
166 }
167
168 return matches;
169}
170
171QStringList SyntaxTree::tokenize(const QString &text)
172{
173 //TODO: properly tokenize (e.g. "foo bar" should not become ['"foo', 'bar"']a
174 static const QVector<QChar> quoters = QVector<QChar>() << '"' << '\'';
175 QStringList tokens;
176 QString acc;
177 QChar closer;
178 for (int i = 0; i < text.size(); ++i) {
179 const QChar c = text.at(i);
180 if (c == '\\') {
181 ++i;
182 if (i < text.size()) {
183 acc.append(text.at(i));
184 }
185 } else if (!closer.isNull()) {
186 if (c == closer) {
187 acc = acc.trimmed();
188 if (!acc.isEmpty()) {
189 tokens << acc;
190 }
191 acc.clear();
192 closer = QChar();
193 } else {
194 acc.append(c);
195 }
196 } else if (c.isSpace()) {
197 acc = acc.trimmed();
198 if (!acc.isEmpty()) {
199 tokens << acc;
200 }
201 acc.clear();
202 } else if (quoters.contains(c)) {
203 closer = c;
204 } else {
205 acc.append(c);
206 }
207 }
208
209 acc = acc.trimmed();
210 if (!acc.isEmpty()) {
211 tokens << acc;
212 }
213
214 return tokens;
215}
216
diff --git a/akonadish/syntaxtree.h b/akonadish/syntaxtree.h
new file mode 100644
index 0000000..884a10d
--- /dev/null
+++ b/akonadish/syntaxtree.h
@@ -0,0 +1,80 @@
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 "state.h"
23
24#include <QStringList>
25#include <QTime>
26#include <QVector>
27
28#include <functional>
29
30class Syntax
31{
32public:
33 typedef QVector<Syntax> List;
34
35 enum Interactivity {
36 NotInteractive = 0,
37 EventDriven
38 };
39
40 Syntax();
41 Syntax(const QString &keyword,
42 const QString &helpText = QString(),
43 std::function<bool(const QStringList &, State &)> lambda = std::function<bool(const QStringList &, State &)>(),
44 Interactivity interactivity = NotInteractive);
45
46 QString keyword;
47 QString help;
48 Interactivity interactivity;
49 std::function<bool(const QStringList &, State &)> lambda;
50 std::function<QStringList(const QStringList &, const QString &)> completer;
51
52 QVector<Syntax> children;
53};
54
55class SyntaxTree
56{
57public:
58 typedef std::pair<const Syntax *, QStringList> Command;
59
60 static SyntaxTree *self();
61
62 int registerSyntax(std::function<Syntax::List()> f);
63 Syntax::List syntax() const;
64 Command match(const QStringList &commands) const;
65 Syntax::List nearestSyntax(const QStringList &words, const QString &fragment) const;
66
67 bool run(const QStringList &commands);
68
69 static QStringList tokenize(const QString &text);
70
71private:
72 SyntaxTree();
73
74 Syntax::List m_syntax;
75 State m_state;
76 QTime m_timeElapsed;
77 static SyntaxTree *s_module;
78};
79
80#define REGISTER_SYNTAX(name) static const int theTrickFor##name = SyntaxTree::self()->registerSyntax(&name::syntax);
diff --git a/cmake/modules/FindReadline.cmake b/cmake/modules/FindReadline.cmake
new file mode 100644
index 0000000..883ad3f
--- /dev/null
+++ b/cmake/modules/FindReadline.cmake
@@ -0,0 +1,47 @@
1# - Try to find readline include dirs and libraries
2#
3# Usage of this module as follows:
4#
5# find_package(Readline)
6#
7# Variables used by this module, they can change the default behaviour and need
8# to be set before calling find_package:
9#
10# Readline_ROOT_DIR Set this variable to the root installation of
11# readline if the module has problems finding the
12# proper installation path.
13#
14# Variables defined by this module:
15#
16# READLINE_FOUND System has readline, include and lib dirs found
17# Readline_INCLUDE_DIR The readline include directories.
18# Readline_LIBRARY The readline library.
19
20find_path(Readline_ROOT_DIR
21 NAMES include/readline/readline.h
22 )
23
24find_path(Readline_INCLUDE_DIR
25 NAMES readline/readline.h
26 HINTS ${Readline_ROOT_DIR}/include
27 )
28
29find_library(Readline_LIBRARY
30 NAMES readline
31 HINTS ${Readline_ROOT_DIR}/lib
32 )
33
34if(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY)
35 set(READLINE_FOUND TRUE)
36else(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY)
37 FIND_LIBRARY(Readline_LIBRARY NAMES readline)
38 include(FindPackageHandleStandardArgs)
39 FIND_PACKAGE_HANDLE_STANDARD_ARGS(Readline DEFAULT_MSG Readline_INCLUDE_DIR Readline_LIBRARY )
40 MARK_AS_ADVANCED(Readline_INCLUDE_DIR Readline_LIBRARY)
41endif(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY)
42
43mark_as_advanced(
44 Readline_ROOT_DIR
45 Readline_INCLUDE_DIR
46 Readline_LIBRARY
47 )