reapack

Package manager for REAPER
Log | Files | Refs | Submodules | README | LICENSE

commit b1debf4048872afa18a1b2ce3421ce19f8332eda
parent 1db43560bba41011db8cddb7c498610eba1d2282
Author: cfillion <cfillion@users.noreply.github.com>
Date:   Mon,  5 Jun 2017 21:38:47 -0400

import: reimplement import logic to handle multiple repositories at once

Diffstat:
Msrc/api.cpp | 12++----------
Msrc/encoding.hpp | 4++--
Msrc/import.cpp | 228+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Msrc/import.hpp | 22++++++++++++++++++----
Msrc/main.cpp | 6+++---
Msrc/reapack.cpp | 15+++++++++++++--
Msrc/reapack.hpp | 2+-
7 files changed, 169 insertions(+), 120 deletions(-)

diff --git a/src/api.cpp b/src/api.cpp @@ -341,16 +341,8 @@ autoInstall: usually set to 2 (obey user setting).)", return false; } - if(commit) { - if(reapack->hasTransaction()) - reapack->setupTransaction()->runTasks(); - else { - reapack->refreshManager(); - reapack->refreshBrowser(); - } - - reapack->config()->write(); - } + if(commit) + reapack->commit(); return true; }); diff --git a/src/encoding.hpp b/src/encoding.hpp @@ -47,9 +47,9 @@ typedef std::string auto_string; #define AUTO_STR(text) text #define to_autostring std::to_string #define auto_snprintf snprintf -#define make_autostring(string) string +#define make_autostring(string) (string) #define make_autocstring(cstr) cstr -#define from_autostring(string) string +#define from_autostring(string) (string) #endif diff --git a/src/import.cpp b/src/import.cpp @@ -35,7 +35,7 @@ static const auto_char *TITLE = AUTO_STR("ReaPack: Import repositories"); static const string DISCOVER_URL = "https://reapack.com/repos"; Import::Import(ReaPack *reapack) - : Dialog(IDD_IMPORT_DIALOG), m_reapack(reapack), m_download(nullptr) + : Dialog(IDD_IMPORT_DIALOG), m_reapack(reapack), m_pool(nullptr), m_state(OK) { } @@ -65,10 +65,12 @@ void Import::onCommand(const int id, int) openURL(DISCOVER_URL); break; case IDCANCEL: - if(m_download) - m_download->abort(); - - close(); + if(m_pool) { + m_pool->abort(); + m_state = Close; + } + else + close(); break; } } @@ -81,133 +83,163 @@ void Import::onTimer(int) #endif } -void Import::fetch() +ThreadPool *Import::setupPool() { - if(m_download) - return; + if(!m_pool) { + m_state = OK; + m_pool = new ThreadPool; - string url = getText(m_url); - boost::algorithm::trim(url); + m_pool->onAbort([=] { if(!m_state) m_state = Aborted; }); + m_pool->onDone([=] { + setWaiting(false); - if(url.empty()) { - close(); - return; - } + if(m_state == OK) + processQueue(); - setWaiting(true); + queue<ImportData> clear; + m_queue.swap(clear); - const auto &opts = m_reapack->config()->network; - MemoryDownload *dl = m_download = new MemoryDownload(url, opts); - - dl->onFinish([=] { - const ThreadTask::State state = dl->state(); - if(state == ThreadTask::Aborted) { - // at this point `this` is deleted, so there is nothing else - // we can do without crashing - return; - } + delete m_pool; + m_pool = nullptr; - setWaiting(false); + if(m_state == Close) + close(); + else + SetFocus(m_url); + }); + } - if(state != ThreadTask::Success) { - const string msg = "Download failed: " + dl->error().message; - MessageBox(handle(), make_autostring(msg).c_str(), TITLE, MB_OK); - SetFocus(m_url); - return; - } + return m_pool; +} - read(); - }); +void Import::fetch() +{ + if(m_pool) // ignore repeated presses on OK + return; - dl->setCleanupHandler([=] { - // if we are still alive - if(dl->state() != ThreadTask::Aborted) - m_download = nullptr; + const auto &opts = m_reapack->config()->network; - delete dl; - }); + stringstream stream(getText(m_url)); + string url; + while(getline(stream, url)) { + boost::algorithm::trim(url); + + if(url.empty()) + continue; + + MemoryDownload *dl = new MemoryDownload(url, opts); + + dl->onFinish([=] { + switch(dl->state()) { + case ThreadTask::Success: + // copy for later use, as `dl` won't be around after this callback + if(!read(dl)) + m_pool->abort(); + break; + case ThreadTask::Failure: { + auto_char msg[1024]; + auto_snprintf(msg, auto_size(msg), + AUTO_STR("Download failed: %s\r\n\r\nURL: %s"), + make_autostring(dl->error().message).c_str(), make_autostring(url).c_str()); + MessageBox(handle(), msg, TITLE, MB_OK); + m_pool->abort(); + break; + } + default: + break; + } + }); + + setupPool()->push(dl); + } - dl->start(); + if(m_pool) + setWaiting(true); + else + close(); } -void Import::read() +bool Import::read(MemoryDownload *dl) { - assert(m_download); + auto_char msg[1024]; try { - IndexPtr index = Index::load({}, m_download->contents().c_str()); - close(import({index->name(), m_download->url()})); + IndexPtr index = Index::load({}, dl->contents().c_str()); + Remote remote = m_reapack->remote(index->name()); + const bool exists = !remote.isNull(); + + if(exists && remote.url() != dl->url()) { + if(remote.isProtected()) { + auto_snprintf(msg, auto_size(msg), + AUTO_STR("The repository %s is protected and cannot be overwritten."), + make_autostring(index->name()).c_str()); + MessageBox(handle(), msg, TITLE, MB_OK); + return false; + } + else { + auto_snprintf(msg, auto_size(msg), + AUTO_STR("%s is already configured with a different URL.\r\n") + AUTO_STR("Do you want to overwrite it?"), + make_autostring(index->name()).c_str()); + + const auto answer = MessageBox(handle(), msg, TITLE, MB_YESNO); + + if(answer != IDYES) + return true; + } + } + else if(exists && remote.isEnabled()) + return true; // nothing to do + + remote.setName(index->name()); + remote.setUrl(dl->url()); + m_queue.push({remote, dl->contents()}); + + return true; } catch(const reapack_error &e) { - const string msg = "The received file is invalid: " + string(e.what()); - MessageBox(handle(), make_autostring(msg).c_str(), TITLE, MB_OK); - SetFocus(m_url); + auto_snprintf(msg, auto_size(msg), + AUTO_STR("The received file is invalid: %s\r\n%s"), + make_autostring(string(e.what())).c_str(), + make_autostring(dl->url()).c_str()); + MessageBox(handle(), msg, TITLE, MB_OK); + return false; } } -bool Import::import(const Remote &remote) +void Import::processQueue() { - auto_char msg[1024]; - - if(const Remote &existing = m_reapack->remote(remote.name())) { - if(existing.isProtected()) { - MessageBox(handle(), - AUTO_STR("This repository is protected and cannot be overwritten."), - TITLE, MB_OK); - - return true; - } - else if(existing.url() != remote.url()) { - auto_snprintf(msg, auto_size(msg), - AUTO_STR("%s is already configured with a different URL.\r\n") - AUTO_STR("Do you want to overwrite it?"), - make_autostring(remote.name()).c_str()); - - const auto answer = MessageBox(handle(), msg, TITLE, MB_YESNO); - - if(answer != IDYES) - return false; - } - else if(existing.isEnabled()) { - auto_snprintf(msg, auto_size(msg), - AUTO_STR("%s is already configured.\r\nNothing to do!"), - make_autostring(remote.name()).c_str()); - MessageBox(handle(), msg, TITLE, MB_OK); - - return true; - } - else { - Transaction *tx = m_reapack->setupTransaction(); + bool ok = true; - if(!tx) - return true; + while(!m_queue.empty()) { + if(!import(m_queue.front())) + ok = false; - m_reapack->enable(existing); - tx->runTasks(); + m_queue.pop(); + } - m_reapack->config()->write(); + if(ok) + m_state = Close; - auto_snprintf(msg, auto_size(msg), AUTO_STR("%s has been enabled."), - make_autostring(remote.name()).c_str()); - MessageBox(handle(), msg, TITLE, MB_OK); + m_reapack->commit(); +} +bool Import::import(const ImportData &data) +{ + if(!data.remote.isEnabled()) { + if(m_reapack->setupTransaction()) { + m_reapack->enable(data.remote); return true; } + else + return false; } Config *config = m_reapack->config(); - config->remotes.add(remote); + config->remotes.add(data.remote); config->write(); - FS::write(Index::pathFor(remote.name()), m_download->contents()); - - auto_snprintf(msg, auto_size(msg), - AUTO_STR("%s has been successfully imported into your repository list."), - make_autostring(remote.name()).c_str()); - MessageBox(handle(), msg, TITLE, MB_OK); - - m_reapack->refreshManager(); - m_reapack->refreshBrowser(); + FS::write(Index::pathFor(data.remote.name()), data.contents); return true; } diff --git a/src/import.hpp b/src/import.hpp @@ -21,12 +21,14 @@ #include "dialog.hpp" #include "encoding.hpp" +#include "remote.hpp" +#include <queue> #include <string> class MemoryDownload; class ReaPack; -class Remote; +class ThreadPool; class Import : public Dialog { @@ -39,13 +41,25 @@ protected: void onTimer(int) override; private: + enum State { + OK, + Aborted, + Close, + }; + + struct ImportData { Remote remote; std::string contents; }; + + ThreadPool *setupPool(); void fetch(); - void read(); - bool import(const Remote &); + bool read(MemoryDownload *); + void processQueue(); + bool import(const ImportData &); void setWaiting(bool); ReaPack *m_reapack; - MemoryDownload *m_download; + ThreadPool *m_pool; + State m_state; + std::queue<ImportData> m_queue; short m_fakePos; HWND m_url; diff --git a/src/main.cpp b/src/main.cpp @@ -92,12 +92,12 @@ static void menuHook(const char *name, HMENU handle, int f) menu.addAction(AUTO_STR("&Browse packages..."), NamedCommandLookup("_REAPACK_BROWSE")); - menu.addAction(AUTO_STR("&Manage repositories..."), - NamedCommandLookup("_REAPACK_MANAGE")); - menu.addAction(AUTO_STR("&Import repositories..."), NamedCommandLookup("_REAPACK_IMPORT")); + menu.addAction(AUTO_STR("&Manage repositories..."), + NamedCommandLookup("_REAPACK_MANAGE")); + menu.addSeparator(); auto_char aboutLabel[32]; diff --git a/src/reapack.cpp b/src/reapack.cpp @@ -182,7 +182,6 @@ void ReaPack::setRemoteEnabled(const bool enable, const Remote &remote) return; m_config->remotes.add(copy); - refreshManager(); }); } @@ -331,7 +330,6 @@ Transaction *ReaPack::setupTransaction() Dialog::Show<Report>(m_instance, m_mainWindow, *m_tx->receipt()); } - }); m_tx->setObsoleteHandler([=] (vector<Registry::Entry> &entries) { @@ -356,6 +354,19 @@ void ReaPack::teardownTransaction() // Update the browser only after the transaction is deleted because // it must be able to start a new one to load the indexes refreshBrowser(); + refreshManager(); +} + +void ReaPack::commit() +{ + if(m_tx) + m_tx->runTasks(); + else { + refreshBrowser(); + refreshManager(); + } + + m_config->write(); } void ReaPack::refreshManager() diff --git a/src/reapack.hpp b/src/reapack.hpp @@ -77,8 +77,8 @@ public: Remote remote(const std::string &name) const; - bool hasTransaction() const { return m_tx != nullptr; } Transaction *setupTransaction(); + void commit(); Config *config() const { return m_config; } private: