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:
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: