commit 7ace89cd3b7bdf8fad5206e5f72b4a2c1b881982
parent bcd6d772988277f6e02d6f8a8e62f6945738717e
Author: cfillion <cfillion@users.noreply.github.com>
Date: Mon, 25 Jan 2016 22:40:38 -0500
register and unregister scripts in the action list
Diffstat:
8 files changed, 265 insertions(+), 96 deletions(-)
diff --git a/src/database.cpp b/src/database.cpp
@@ -67,9 +67,9 @@ reapack_error Database::lastError() const
return reapack_error(sqlite3_errmsg(m_db));
}
-uint64_t Database::lastInsertId() const
+int Database::lastInsertId() const
{
- return (uint64_t)sqlite3_last_insert_rowid(m_db);
+ return (int)sqlite3_last_insert_rowid(m_db);
}
int Database::version() const
diff --git a/src/database.hpp b/src/database.hpp
@@ -37,7 +37,7 @@ public:
Statement *prepare(const char *sql);
void exec(const char *sql);
- uint64_t lastInsertId() const;
+ int lastInsertId() const;
int version() const;
int errorCode() const;
void begin();
diff --git a/src/registry.cpp b/src/registry.cpp
@@ -23,7 +23,6 @@
#include "path.hpp"
#include "remote.hpp"
-#include <reaper_plugin_functions.h>
#include <sqlite3.h>
using namespace std;
@@ -35,21 +34,37 @@ Registry::Registry(const Path &path)
// entry queries
m_insertEntry = m_db.prepare(
- "INSERT OR REPLACE INTO entries "
- "VALUES(NULL, ?, ?, ?, ?);"
+ "INSERT INTO entries VALUES(NULL, ?, ?, ?, ?, ?, NULL);"
+ );
+
+ m_updateEntry = m_db.prepare(
+ "UPDATE entries SET type = ?, version = ? WHERE id = ?"
);
m_findEntry = m_db.prepare(
- "SELECT id, version FROM entries "
+ "SELECT id, remote, category, package, type, version FROM entries "
"WHERE remote = ? AND category = ? AND package = ? "
"LIMIT 1"
);
- m_allEntries = m_db.prepare("SELECT id, version FROM entries WHERE remote = ?");
+ m_allEntries = m_db.prepare(
+ "SELECT id, remote, category, package, type "
+ "FROM entries WHERE remote = ?"
+ );
m_forgetEntry = m_db.prepare("DELETE FROM entries WHERE id = ?");
+ m_setMainFile = m_db.prepare(
+ "UPDATE entries SET mainFile = ("
+ " SELECT id FROM files WHERE path = ?"
+ ") WHERE id = ?"
+ );
// file queries
m_getFiles = m_db.prepare("SELECT path FROM files WHERE entry = ?");
+ m_getMainFile = m_db.prepare(
+ "SELECT path FROM files WHERE id = ("
+ " SELECT mainFile FROM entries WHERE id = ? LIMIT 1"
+ ") LIMIT 1"
+ );
m_insertFile = m_db.prepare("INSERT INTO files VALUES(NULL, ?, ?)");
m_clearFiles = m_db.prepare(
"DELETE FROM files WHERE entry = ("
@@ -81,7 +96,9 @@ void Registry::migrate()
" remote TEXT NOT NULL,"
" category TEXT NOT NULL,"
" package TEXT NOT NULL,"
+ " type INTEGER NOT NULL,"
" version INTEGER NOT NULL,"
+ " mainFile INTEGER,"
" UNIQUE(remote, category, package)"
");"
@@ -103,7 +120,7 @@ void Registry::migrate()
m_db.commit();
}
-void Registry::push(Version *ver, vector<Path> *conflicts)
+auto Registry::push(Version *ver, vector<Path> *conflicts) -> Entry
{
m_savepoint->exec();
bool hasConflicts = false;
@@ -117,17 +134,29 @@ void Registry::push(Version *ver, vector<Path> *conflicts)
m_clearFiles->bind(3, pkg->name());
m_clearFiles->exec();
- m_insertEntry->bind(1, ri->name());
- m_insertEntry->bind(2, cat->name());
- m_insertEntry->bind(3, pkg->name());
- m_insertEntry->bind(4, ver->code());
- m_insertEntry->exec();
+ int entryId = getEntry(ver->package()).id;
- const uint64_t entryId = m_db.lastInsertId();
+ if(entryId) {
+ m_updateEntry->bind(1, pkg->type());
+ m_updateEntry->bind(2, ver->code());
+ m_updateEntry->bind(3, entryId);
+ m_updateEntry->exec();
+ }
+ else {
+ m_insertEntry->bind(1, ri->name());
+ m_insertEntry->bind(2, cat->name());
+ m_insertEntry->bind(3, pkg->name());
+ m_insertEntry->bind(4, pkg->type());
+ m_insertEntry->bind(5, ver->code());
+ m_insertEntry->exec();
+
+ entryId = m_db.lastInsertId();
+ }
for(const Path &path : ver->files()) {
m_insertFile->bind(1, entryId);
m_insertFile->bind(2, path.join('/'));
+
try {
m_insertFile->exec();
}
@@ -143,15 +172,43 @@ void Registry::push(Version *ver, vector<Path> *conflicts)
}
}
- if(hasConflicts)
+ if(ver->mainSource()) {
+ m_setMainFile->bind(1, ver->mainSource()->targetPath().join('/'));
+ m_setMainFile->bind(2, entryId);
+ m_setMainFile->exec();
+ }
+
+ if(hasConflicts) {
m_restore->exec();
- else
+ return {};
+ }
+ else {
m_release->exec();
+ return {entryId, ri->name(), cat->name(),
+ pkg->name(), pkg->type(), ver->code()};
+ }
}
-Registry::Entry Registry::query(Package *pkg) const
+auto Registry::query(Package *pkg) const -> QueryResult
+{
+ const Entry &entry = getEntry(pkg);
+
+ if(!entry.id)
+ return {};
+
+ const Status status =
+ entry.version == pkg->lastVersion()->code() ? UpToDate : UpdateAvailable;
+
+ return {status, entry};
+}
+
+auto Registry::getEntry(Package *pkg) const -> Entry
{
int id = 0;
+ string remote;
+ string category;
+ string package;
+ Package::Type type = Package::UnknownType;
uint64_t version = 0;
Category *cat = pkg->category();
@@ -162,44 +219,53 @@ Registry::Entry Registry::query(Package *pkg) const
m_findEntry->bind(3, pkg->name());
m_findEntry->exec([&] {
- id = m_findEntry->intColumn(0);
- version = m_findEntry->uint64Column(1);
- return false;
- });
+ int col = 0;
- if(!id)
- return {id, Uninstalled, 0};
+ id = m_findEntry->intColumn(col++);
+ remote = m_findEntry->stringColumn(col++);
+ category = m_findEntry->stringColumn(col++);
+ package = m_findEntry->stringColumn(col++);
+ type = static_cast<Package::Type>(m_findEntry->intColumn(col++));
+ version = m_findEntry->uint64Column(col++);
- Version *lastVer = pkg->lastVersion();
+ return false;
+ });
- const Status status = version == lastVer->code() ? UpToDate : UpdateAvailable;
- return {id, status, version};
+ return {id, remote, category, package, type, version};
}
-vector<Registry::Entry> Registry::queryAll(const Remote &remote) const
+auto Registry::getEntries(const Remote &remote) const -> vector<Entry>
{
vector<Registry::Entry> list;
m_allEntries->bind(1, remote.name());
m_allEntries->exec([&] {
- const int id = m_allEntries->intColumn(0);
- const uint64_t version = m_allEntries->uint64Column(1);
+ int col = 0;
+
+ const int id = m_allEntries->intColumn(col++);
+ const string &remote = m_allEntries->stringColumn(col++);
+ const string &category = m_allEntries->stringColumn(col++);
+ const string &package = m_allEntries->stringColumn(col++);
+ const Package::Type type =
+ static_cast<Package::Type>(m_allEntries->intColumn(col++));
+ const uint64_t version = m_allEntries->uint64Column(col++);
+
+ list.push_back({id, remote, category, package, type, version});
- list.push_back({id, Unknown, version});
return true;
});
return list;
}
-set<Path> Registry::getFiles(const Entry &qr) const
+set<Path> Registry::getFiles(const Entry &entry) const
{
- if(!qr.id) // skip processing for new packages
+ if(!entry.id) // skip processing for new packages
return {};
set<Path> list;
- m_getFiles->bind(1, qr.id);
+ m_getFiles->bind(1, entry.id);
m_getFiles->exec([&] {
list.insert(m_getFiles->stringColumn(0));
return true;
@@ -208,12 +274,28 @@ set<Path> Registry::getFiles(const Entry &qr) const
return list;
}
-void Registry::forget(const Entry &qr)
+string Registry::getMainFile(const Entry &entry) const
+{
+ if(!entry.id)
+ return {};
+
+ string mainFile;
+
+ m_getMainFile->bind(1, entry.id);
+ m_getMainFile->exec([&] {
+ mainFile = m_getMainFile->stringColumn(0);
+ return false;
+ });
+
+ return mainFile;
+}
+
+void Registry::forget(const Entry &entry)
{
- m_forgetFiles->bind(1, qr.id);
+ m_forgetFiles->bind(1, entry.id);
m_forgetFiles->exec();
- m_forgetEntry->bind(1, qr.id);
+ m_forgetEntry->bind(1, entry.id);
m_forgetEntry->exec();
}
diff --git a/src/registry.hpp b/src/registry.hpp
@@ -23,6 +23,7 @@
#include "path.hpp"
#include "database.hpp"
+#include "package.hpp"
class Package;
class Path;
@@ -34,22 +35,31 @@ public:
Registry(const Path &path = Path());
enum Status {
- Unknown,
Uninstalled,
UpdateAvailable,
UpToDate,
};
struct Entry {
- int id; // internal use
- Status status;
+ int id;
+ std::string remote;
+ std::string category;
+ std::string package;
+ Package::Type type;
uint64_t version;
};
- Entry query(Package *) const;
- std::vector<Entry> queryAll(const Remote &) const;
+ struct QueryResult {
+ Status status;
+ Entry entry;
+ };
+
+ QueryResult query(Package *) const;
+ Entry getEntry(Package *) const;
+ std::vector<Entry> getEntries(const Remote &) const;
std::set<Path> getFiles(const Entry &) const;
- void push(Version *, std::vector<Path> *conflicts = nullptr);
+ std::string getMainFile(const Entry &) const;
+ Entry push(Version *, std::vector<Path> *conflicts = nullptr);
void forget(const Entry &);
void commit();
@@ -58,11 +68,14 @@ private:
Database m_db;
Statement *m_insertEntry;
+ Statement *m_updateEntry;
Statement *m_findEntry;
Statement *m_allEntries;
Statement *m_forgetEntry;
+ Statement *m_setMainFile;
Statement *m_getFiles;
+ Statement *m_getMainFile;
Statement *m_insertFile;
Statement *m_clearFiles;
Statement *m_forgetFiles;
diff --git a/src/report.cpp b/src/report.cpp
@@ -82,7 +82,7 @@ void Report::printNewPackages()
{
printHeader("New packages");
- for(const Transaction::PackageEntry &entry : m_transaction->newPackages()) {
+ for(const Transaction::InstallTicket &entry : m_transaction->newPackages()) {
Version *ver = entry.first;
m_stream << NL << ver->fullName() << NL;
}
@@ -92,13 +92,13 @@ void Report::printUpdates()
{
printHeader("Updates");
- for(const Transaction::PackageEntry &entry : m_transaction->updates()) {
+ for(const Transaction::InstallTicket &entry : m_transaction->updates()) {
Package *pkg = entry.first->package();
- const Registry::Entry ®Entry = entry.second;
+ const auto &queryRes = entry.second;
const VersionSet &versions = pkg->versions();
for(Version *ver : versions | boost::adaptors::reversed) {
- if(ver->code() <= regEntry.version)
+ if(ver->code() <= queryRes.entry.version)
break;
m_stream << NL << ver->fullName() << NL;
diff --git a/src/transaction.cpp b/src/transaction.cpp
@@ -90,11 +90,11 @@ void Transaction::upgradeAll(Download *dl)
void Transaction::upgrade(Package *pkg)
{
Version *ver = pkg->lastVersion();
- Registry::Entry entry = m_registry->query(pkg);
+ auto queryRes = m_registry->query(pkg);
try {
vector<Path> conflicts;
- m_registry->push(ver, &conflicts);
+ queryRes.entry = m_registry->push(ver, &conflicts);
if(!conflicts.empty()) {
for(const Path &path : conflicts) {
@@ -111,36 +111,38 @@ void Transaction::upgrade(Package *pkg)
return;
}
- if(entry.status == Registry::UpToDate) {
+ if(queryRes.status == Registry::UpToDate) {
if(allFilesExists(ver->files()))
return;
else
- entry.status = Registry::Uninstalled;
+ queryRes.status = Registry::Uninstalled;
}
- m_installQueue.push({ver, entry});
+ m_installQueue.push({ver, queryRes});
}
void Transaction::install()
{
while(!m_installQueue.empty()) {
- const PackageEntry entry = m_installQueue.front();
+ const InstallTicket ticket = m_installQueue.front();
m_installQueue.pop();
- Version *ver = entry.first;
- const Registry::Entry regEntry = entry.second;
- const set<Path> ¤tFiles = m_registry->getFiles(regEntry);
+ Version *ver = ticket.first;
+ const Registry::QueryResult queryRes = ticket.second;
+ const set<Path> ¤tFiles = m_registry->getFiles(queryRes.entry);
InstallTask *task = new InstallTask(ver, currentFiles, this);
task->onCommit([=] {
- if(regEntry.status == Registry::UpdateAvailable)
- m_updates.push_back(entry);
+ if(queryRes.status == Registry::UpdateAvailable)
+ m_updates.push_back(ticket);
else
- m_new.push_back(entry);
+ m_new.push_back(ticket);
const set<Path> &removedFiles = task->removedFiles();
m_removals.insert(removedFiles.begin(), removedFiles.end());
+
+ registerInHost(true, queryRes.entry);
});
addTask(task);
@@ -149,17 +151,25 @@ void Transaction::install()
runTasks();
}
-void Transaction::registerAll(const Remote &)
+void Transaction::registerAll(const Remote &remote)
{
+ const vector<Registry::Entry> &entries = m_registry->getEntries(remote);
+
+ for(const auto &entry : entries)
+ registerInHost(true, entry);
}
-void Transaction::unregisterAll(const Remote &)
+void Transaction::unregisterAll(const Remote &remote)
{
+ const vector<Registry::Entry> &entries = m_registry->getEntries(remote);
+
+ for(const auto &entry : entries)
+ registerInHost(false, entry);
}
void Transaction::uninstall(const Remote &remote)
{
- const vector<Registry::Entry> &entries = m_registry->queryAll(remote);
+ const vector<Registry::Entry> &entries = m_registry->getEntries(remote);
if(entries.empty())
return;
@@ -170,6 +180,8 @@ void Transaction::uninstall(const Remote &remote)
const set<Path> &files = m_registry->getFiles(entry);
allFiles.insert(allFiles.end(), files.begin(), files.end());
+ registerInHost(false, entry);
+
// forget the package even if some files cannot be removed
m_registry->forget(entry);
}
@@ -229,6 +241,8 @@ void Transaction::finish()
task->commit();
m_registry->commit();
+
+ registerScriptsInHost();
}
m_onFinish();
@@ -266,3 +280,37 @@ void Transaction::runTasks()
if(m_downloadQueue.idle())
finish();
}
+
+void Transaction::registerInHost(const bool add, const Registry::Entry &entry)
+{
+ // don't actually do anything until commit()
+
+ switch(entry.type) {
+ case Package::ScriptType:
+ m_scriptRegs.push({add, entry, m_registry->getMainFile(entry)});
+ break;
+ default:
+ break;
+ }
+}
+
+void Transaction::registerScriptsInHost()
+{
+ if(!AddRemoveReaScript) {
+ // do nothing if REAPER < v5.12
+ m_scriptRegs = {};
+ return;
+ }
+
+ while(!m_scriptRegs.empty()) {
+ const HostRegistration ® = m_scriptRegs.front();
+ const std::string &path = Path::prefixRoot(reg.file).join();
+ const bool isLast = m_scriptRegs.size() == 1;
+
+ if(!AddRemoveReaScript(reg.add, 0, path.c_str(), isLast) && reg.add)
+ addError("Script could not be registered in REAPER.", reg.file);
+
+ m_scriptRegs.pop();
+ }
+}
+
diff --git a/src/transaction.hpp b/src/transaction.hpp
@@ -36,8 +36,8 @@ public:
typedef boost::signals2::signal<void ()> Signal;
typedef Signal::slot_type Callback;
- typedef std::pair<Version *, const Registry::Entry> PackageEntry;
- typedef std::vector<PackageEntry> PackageEntryList;
+ typedef std::pair<Version *, const Registry::QueryResult> InstallTicket;
+ typedef std::vector<InstallTicket> InstallTicketList;
struct Error {
std::string message;
@@ -63,14 +63,13 @@ public:
DownloadQueue *downloadQueue() { return &m_downloadQueue; }
size_t taskCount() const { return m_tasks.size(); }
- const PackageEntryList &newPackages() const { return m_new; }
- const PackageEntryList &updates() const { return m_updates; }
+ const InstallTicketList &newPackages() const { return m_new; }
+ const InstallTicketList &updates() const { return m_updates; }
const std::set<Path> &removals() const { return m_removals; }
const ErrorList &errors() const { return m_errors; }
bool saveFile(Download *, const Path &);
void addError(const std::string &msg, const std::string &title);
- Path prefixPath(const Path &) const;
private:
void install();
@@ -81,18 +80,24 @@ private:
bool allFilesExists(const std::set<Path> &) const;
void addTask(Task *);
+ void registerInHost(bool add, const Registry::Entry &);
+ void registerScriptsInHost();
+
bool m_isCancelled;
Registry *m_registry;
std::set<Remote> m_remotes;
std::vector<RemoteIndex *> m_remoteIndexes;
DownloadQueue m_downloadQueue;
- std::queue<PackageEntry> m_installQueue;
+ std::queue<InstallTicket> m_installQueue;
std::vector<Task *> m_tasks;
std::queue<Task *> m_taskQueue;
- PackageEntryList m_new;
- PackageEntryList m_updates;
+ struct HostRegistration { bool add; Registry::Entry entry; std::string file; };
+ std::queue<HostRegistration> m_scriptRegs;
+
+ InstallTicketList m_new;
+ InstallTicketList m_updates;
std::set<Path> m_removals;
ErrorList m_errors;
diff --git a/test/registry.cpp b/test/registry.cpp
@@ -15,7 +15,7 @@ static const char *M = "[registry]";
#define MAKE_PACKAGE \
RemoteIndex ri("Remote Name"); \
- Category cat("Hello", &ri); \
+ Category cat("Category Name", &ri); \
Package pkg(Package::ScriptType, "Hello", &cat); \
Version *ver = new Version("1.0", &pkg); \
Source *src = new Source(Source::GenericPlatform, "file", "url", ver); \
@@ -27,20 +27,28 @@ TEST_CASE("query uninstalled package", M) {
Registry reg;
- const Registry::Entry res = reg.query(&pkg);
+ const auto &res = reg.query(&pkg);
REQUIRE(res.status == Registry::Uninstalled);
- REQUIRE(res.version == 0);
+ REQUIRE(res.entry.id == 0);
+ REQUIRE(res.entry.version == 0);
}
TEST_CASE("query up to date pacakge", M) {
MAKE_PACKAGE
Registry reg;
- reg.push(ver);
- const Registry::Entry res = reg.query(&pkg);
- REQUIRE(res.status == Registry::UpToDate);
- REQUIRE(res.version == Version("1.0").code());
+ const Registry::Entry &entry = reg.push(ver);
+ REQUIRE(entry.id == 1);
+ REQUIRE(entry.remote == "Remote Name");
+ REQUIRE(entry.category == "Category Name");
+ REQUIRE(entry.package == "Hello");
+ REQUIRE(entry.type == Package::ScriptType);
+ REQUIRE(entry.version == Version("1.0").code());
+
+ const Registry::QueryResult &queryRes = reg.query(&pkg);
+ REQUIRE(queryRes.status == Registry::UpToDate);
+ REQUIRE(queryRes.entry.version == Version("1.0").code());
}
TEST_CASE("bump version", M) {
@@ -53,26 +61,27 @@ TEST_CASE("bump version", M) {
reg.push(ver);
pkg.addVersion(ver2);
- const Registry::Entry res1 = reg.query(&pkg);
+ const Registry::QueryResult &res1 = reg.query(&pkg);
REQUIRE(res1.status == Registry::UpdateAvailable);
- REQUIRE(res1.version == Version("1.0").code());
+ REQUIRE(res1.entry.version == Version("1.0").code());
reg.push(ver2);
- const Registry::Entry res2 = reg.query(&pkg);
+ const Registry::QueryResult &res2 = reg.query(&pkg);
REQUIRE(res2.status == Registry::UpToDate);
- REQUIRE(res2.version == Version("2.0").code());
+ REQUIRE(res2.entry.version == Version("2.0").code());
+
+ REQUIRE(res2.entry.id == res1.entry.id);
}
TEST_CASE("get file list", M) {
MAKE_PACKAGE
Registry reg;
- REQUIRE(reg.getFiles(reg.query(&pkg)).empty());
+ REQUIRE(reg.getFiles(reg.query(&pkg).entry).empty());
reg.push(ver);
- const Registry::Entry res = reg.query(&pkg);
- const set<Path> files = reg.getFiles(res);
+ const set<Path> &files = reg.getFiles(reg.query(&pkg).entry);
REQUIRE(files == ver->files());
}
@@ -83,29 +92,28 @@ TEST_CASE("query all packages", M) {
const Remote remote("Remote Name", "irrelevent_url");
Registry reg;
- REQUIRE(reg.queryAll(remote).empty());
+ REQUIRE(reg.getEntries(remote).empty());
reg.push(ver);
- const vector<Registry::Entry> entries = reg.queryAll(remote);
+ const vector<Registry::Entry> &entries = reg.getEntries(remote);
REQUIRE(entries.size() == 1);
REQUIRE(entries[0].id == 1);
- REQUIRE(entries[0].status == Registry::Unknown);
- REQUIRE(entries[0].version == ver->code());
+ REQUIRE(entries[0].remote == "Remote Name");
+ REQUIRE(entries[0].category == "Category Name");
+ REQUIRE(entries[0].package == "Hello");
+ REQUIRE(entries[0].type == Package::ScriptType);
}
TEST_CASE("forget registry entry", M) {
MAKE_PACKAGE
Registry reg;
- reg.push(ver);
-
- reg.forget(reg.query(&pkg));
+ reg.forget(reg.push(ver));
- const Registry::Entry afterForget = reg.query(&pkg);
- REQUIRE(afterForget.id == 0);
+ const Registry::QueryResult &afterForget = reg.query(&pkg);
REQUIRE(afterForget.status == Registry::Uninstalled);
- REQUIRE(afterForget.version == 0);
+ REQUIRE(afterForget.entry.id == 0);
}
TEST_CASE("file conflicts", M) {
@@ -117,7 +125,7 @@ TEST_CASE("file conflicts", M) {
}
RemoteIndex ri("Remote Name");
- Category cat("Hello", &ri);
+ Category cat("Category Name", &ri);
Package pkg(Package::ScriptType, "Duplicate Package", &cat);
Version *ver = new Version("1.0", &pkg);
Source *src1 = new Source(Source::GenericPlatform, "file", "url", ver);
@@ -130,7 +138,7 @@ TEST_CASE("file conflicts", M) {
try {
reg.push(ver);
- FAIL("duplicate was accepted");
+ FAIL("duplicate is accepted");
}
catch(const reapack_error &) {}
@@ -144,3 +152,16 @@ TEST_CASE("file conflicts", M) {
REQUIRE(reg.query(&pkg).status == Registry::Uninstalled);
}
+
+TEST_CASE("get main file", M) {
+ MAKE_PACKAGE
+
+ Registry reg;
+ REQUIRE(reg.getMainFile({}).empty());
+
+ Source *main = new Source(Source::GenericPlatform, {}, "url", ver);
+ ver->addSource(main);
+
+ const Registry::Entry &entry = reg.push(ver);
+ REQUIRE(reg.getMainFile(entry) == main->targetPath().join('/'));
+}