commit 2ca608ac2ca23585d3f03bff9cf5d98d3713293d
parent 66e4371564af3b7afae1f32b8799621a8cd5b4f9
Author: cfillion <cfillion@users.noreply.github.com>
Date: Tue, 1 Dec 2015 21:37:20 -0500
prepare the code for configurable repositories
Diffstat:
14 files changed, 358 insertions(+), 86 deletions(-)
diff --git a/Tupfile b/Tupfile
@@ -28,4 +28,5 @@ WDLSOURCE += $(TINYXML)/tinyxmlparser.cpp $(TINYXML)/tinyxmlerror.cpp
: build/*.o |> !link $(SOFLAGS) |> bin/reaper_reapack.dylib
: foreach test/*.cpp |> !build -Isrc |> build/test/%B.o
+: test/helper/*.cpp |> !build -Isrc |> build/test/helper_%B.o
: build/*.o build/test/*.o |> !link |> bin/test
diff --git a/src/config.cpp b/src/config.cpp
@@ -0,0 +1,16 @@
+#include "config.hpp"
+
+#include "path.hpp"
+
+Config::Config()
+{
+}
+
+void Config::read(const Path &path)
+{
+ m_path = path.cjoin();
+}
+
+void Config::write() const
+{
+}
diff --git a/src/config.hpp b/src/config.hpp
@@ -0,0 +1,39 @@
+#ifndef REAPACK_CONFIG_HPP
+#define REAPACK_CONFIG_HPP
+
+#include <string>
+#include <vector>
+
+struct Repository {
+ Repository(const std::string &name, const char *url)
+ : m_name(name), m_url(url)
+ {}
+
+ const std::string &name() const { return m_name; }
+ const char *url() const { return m_url; }
+
+private:
+ const std::string &m_name;
+ const char *m_url;
+};
+
+typedef std::vector<Repository> RepositoryList;
+
+class Path;
+
+class Config {
+public:
+ Config();
+
+ void read(const Path &);
+ void write() const;
+
+ const RepositoryList &repositories() const { return m_repositories; }
+
+private:
+ const char *m_path;
+
+ RepositoryList m_repositories;
+};
+
+#endif
diff --git a/src/main.cpp b/src/main.cpp
@@ -22,7 +22,7 @@ extern "C" REAPER_PLUGIN_DLL_EXPORT int REAPER_PLUGIN_ENTRYPOINT(
reapack.init(instance, rec);
reapack.setupAction("REAPACKSYNC", "ReaPack: Synchronize Packages",
- &reapack.action, std::bind(&ReaPack::synchronize, reapack));
+ &reapack.action, std::bind(&ReaPack::synchronizeAll, reapack));
rec->Register("hookcommand", (void *)commandHook);
diff --git a/src/package.cpp b/src/package.cpp
@@ -41,7 +41,7 @@ VersionPtr Package::lastVersion() const
return *prev(m_versions.end());
}
-InstallLocation Package::targetLocation() const
+Path Package::targetLocation() const
{
switch(m_type) {
case ScriptType:
@@ -51,13 +51,17 @@ InstallLocation Package::targetLocation() const
}
}
-InstallLocation Package::scriptLocation() const
+Path Package::scriptLocation() const
{
// TODO: use actual database name instead of hard-coded "ReaScripts"
- InstallLocation loc("/Scripts/ReaScripts", name());
+ Path path;
+ path.append("Scripts");
+ path.append("ReaScripts");
if(m_category)
- loc.appendDir("/" + category()->name());
+ path.append(category()->name());
- return loc;
+ path.append(m_name);
+
+ return path;
}
diff --git a/src/package.hpp b/src/package.hpp
@@ -1,6 +1,7 @@
#ifndef REAPACK_PACKAGE_HPP
#define REAPACK_PACKAGE_HPP
+#include "path.hpp"
#include "version.hpp"
class Package;
@@ -8,35 +9,6 @@ typedef std::shared_ptr<Package> PackagePtr;
class Category;
-class InstallLocation {
-public:
- InstallLocation(const std::string &d, const std::string &f)
- : m_directory(d), m_filename(f)
- {}
-
- void prependDir(const std::string &p) { m_directory.insert(0, p); }
- void appendDir(const std::string &p) { m_directory.append(p); }
-
- const std::string directory() const { return m_directory; }
- const std::string filename() const { return m_filename; }
-
- std::string fullPath() const { return m_directory + "/" + m_filename; }
-
- bool operator==(const InstallLocation &o) const
- {
- return m_directory == o.directory() && m_filename == o.filename();
- }
-
- bool operator!=(const InstallLocation &o) const
- {
- return !(*this == o);
- }
-
-private:
- std::string m_directory;
- std::string m_filename;
-};
-
class Package {
public:
enum Type {
@@ -59,10 +31,10 @@ public:
VersionPtr version(const int i) const;
VersionPtr lastVersion() const;
- InstallLocation targetLocation() const;
+ Path targetLocation() const;
private:
- InstallLocation scriptLocation() const;
+ Path scriptLocation() const;
Category *m_category;
Type m_type;
diff --git a/src/path.cpp b/src/path.cpp
@@ -0,0 +1,66 @@
+#include "path.hpp"
+
+using namespace std;
+
+#ifndef _WIN32
+static const char SEPARATOR = '/';
+#else
+static const char SEPARATOR = '\\';
+#endif
+
+void Path::prepend(const string &part)
+{
+ m_parts.push_front(part);
+}
+
+void Path::append(const string &part)
+{
+ m_parts.push_back(part);
+}
+
+string Path::join(const bool skipLast) const
+{
+ string path;
+
+ auto end = m_parts.end();
+
+ if(skipLast)
+ end--;
+
+ for(auto it = m_parts.begin(); it != end; it++) {
+ const string &part = *it;
+
+ if(!path.empty())
+ path.insert(path.end(), SEPARATOR);
+
+ path.append(part);
+ }
+
+ return path;
+}
+
+bool Path::operator==(const Path &o) const
+{
+ return m_parts == o.m_parts;
+}
+
+bool Path::operator!=(const Path &o) const
+{
+ return !(*this == o);
+}
+
+Path Path::operator+(const string &part) const
+{
+ Path path(*this);
+ path.append(part);
+
+ return path;
+}
+
+Path Path::operator+(const Path &o) const
+{
+ Path path(*this);
+ path.m_parts.insert(path.m_parts.end(), o.m_parts.begin(), o.m_parts.end());
+
+ return path;
+}
diff --git a/src/path.hpp b/src/path.hpp
@@ -0,0 +1,35 @@
+#ifndef REAPACK_PATH_HPP
+#define REAPACK_PATH_HPP
+
+#include <string>
+#include <list>
+
+class Path {
+public:
+ void prepend(const std::string &part);
+ void append(const std::string &part);
+
+ bool empty() const { return m_parts.empty(); }
+ int size() const { return m_parts.size(); }
+
+ std::string dirname() const { return join(true); }
+ const char *cdirname() const { return dirname().c_str(); }
+
+ const std::string &basename() const { return m_parts.back(); }
+ const char *cbasename() const { return basename().c_str(); }
+
+ std::string join() const { return join(false); }
+ const char *cjoin() const { return join().c_str(); }
+
+ bool operator==(const Path &) const;
+ bool operator!=(const Path &) const;
+ Path operator+(const std::string &) const;
+ Path operator+(const Path &) const;
+
+private:
+ std::string join(const bool) const;
+
+ std::list<std::string> m_parts;
+};
+
+#endif
diff --git a/src/reapack.cpp b/src/reapack.cpp
@@ -13,7 +13,13 @@ void ReaPack::init(REAPER_PLUGIN_HINSTANCE instance, reaper_plugin_info_t *rec)
m_instance = instance;
m_rec = rec;
m_mainHandle = GetMainHwnd();
- m_resourcePath = GetResourcePath();
+ m_resourcePath.append(GetResourcePath());
+
+ m_config.read(m_resourcePath + "reapack.ini");
+
+ m_dbPath = m_resourcePath + "ReaPack";
+
+ RecursiveCreateDirectory(m_dbPath.cjoin(), 0);
}
void ReaPack::setupAction(const char *name, const char *desc,
@@ -36,17 +42,62 @@ bool ReaPack::execActions(const int id, const int)
return true;
}
-void ReaPack::synchronize()
+void ReaPack::synchronizeAll()
{
- try {
- m_database = Database::load("/Users/cfillion/Programs/reapack/reapack.xml");
+ RepositoryList repos = m_config.repositories();
- for(PackagePtr pkg : m_database->packages()) {
- installPackage(pkg);
+ if(repos.empty()) {
+ ShowMessageBox("No repository configured, nothing to do!", "ReaPack", 0);
+ return;
+ }
+
+ for(const Repository &repo : repos) {
+ try {
+ synchronize(repo);
+ }
+ catch(const reapack_error &e) {
+ ShowMessageBox(e.what(), repo.name().c_str(), 0);
}
}
- catch(const reapack_error &e) {
- ShowMessageBox(e.what(), "Database Error", 0);
+}
+
+void ReaPack::synchronize(const Repository &repo)
+{
+ m_downloadQueue.push(repo.url(), [=](const int status, const string &contents) {
+ if(status != 200)
+ return;
+
+ const Path path = m_dbPath + (repo.name() + ".xml");
+
+ ofstream file(path.join());
+ if(file.bad()) {
+ ShowMessageBox(strerror(errno), repo.name().c_str(), 0);
+ return;
+ }
+
+ file << contents;
+ file.close();
+
+ synchronize(Database::load(path.cjoin()));
+ });
+}
+
+void ReaPack::synchronize(DatabasePtr database)
+{
+ if(database->packages().empty()) {
+ ShowMessageBox("The package database is empty, nothing to do!",
+ "ReaPack", 0);
+
+ return;
+ }
+
+ for(PackagePtr pkg : database->packages()) {
+ try {
+ installPackage(pkg);
+ }
+ catch(const reapack_error &e) {
+ ShowMessageBox(e.what(), "Package Error", 0);
+ }
}
}
@@ -60,12 +111,10 @@ void ReaPack::installPackage(PackagePtr pkg)
return;
}
- InstallLocation loc = pkg->targetLocation();
- loc.prependDir(m_resourcePath);
-
- RecursiveCreateDirectory(loc.directory().c_str(), 0);
+ const Path path = m_resourcePath + pkg->targetLocation();
+ RecursiveCreateDirectory(path.cdirname(), 0);
- ofstream file(loc.fullPath());
+ ofstream file(path.join());
if(file.bad()) {
ShowMessageBox(strerror(errno), pkg->name().c_str(), 0);
return;
diff --git a/src/reapack.hpp b/src/reapack.hpp
@@ -4,6 +4,7 @@
#include <functional>
#include <map>
+#include "config.hpp"
#include "database.hpp"
#include "download.hpp"
@@ -21,18 +22,22 @@ public:
gaccel_register_t *action, ActionCallback callback);
bool execActions(const int id, const int);
- void synchronize();
- void installPackage(PackagePtr pkg);
+ void synchronizeAll();
+ void synchronize(const Repository &);
+ void synchronize(DatabasePtr);
+ void installPackage(PackagePtr);
private:
std::map<int, ActionCallback> m_actions;
- DatabasePtr m_database;
+
+ Config m_config;
DownloadQueue m_downloadQueue;
REAPER_PLUGIN_HINSTANCE m_instance;
reaper_plugin_info_t *m_rec;
HWND m_mainHandle;
- const char *m_resourcePath;
+ Path m_resourcePath;
+ Path m_dbPath;
};
#endif
diff --git a/test/helper/io.cpp b/test/helper/io.cpp
@@ -0,0 +1,11 @@
+#include "io.hpp"
+
+#include <path.hpp>
+
+using namespace std;
+
+ostream &operator<<(ostream &os, const Path &path)
+{
+ os << "\"" + path.join() + '"';
+ return os;
+}
diff --git a/test/helper/io.hpp b/test/helper/io.hpp
@@ -0,0 +1,10 @@
+#ifndef REAPACK_TEST_HELPER_IO_HPP
+#define REAPACK_TEST_HELPER_IO_HPP
+
+#include <ostream>
+
+class Path;
+
+std::ostream &operator<<(std::ostream &, const Path &);
+
+#endif
diff --git a/test/package.cpp b/test/package.cpp
@@ -1,5 +1,7 @@
#include <catch.hpp>
+#include "helper/io.hpp"
+
#include <database.hpp>
#include <errors.hpp>
#include <package.hpp>
@@ -50,32 +52,6 @@ TEST_CASE("drop empty version", M) {
REQUIRE(pack.versions().empty());
}
-TEST_CASE("different install location", M) {
- const InstallLocation a{"/hello", "/world"};
- const InstallLocation b{"/chunky", "/bacon"};
-
- REQUIRE_FALSE(a == b);
-}
-
-TEST_CASE("set install location prefix", M) {
- InstallLocation loc{"/hello", "world"};
-
- REQUIRE(loc.directory() == "/hello");
- REQUIRE(loc.filename() == "world");
- REQUIRE(loc.fullPath() == "/hello/world");
-
- loc.prependDir("/root");
-
- REQUIRE(loc.directory() == "/root/hello");
- REQUIRE(loc.fullPath() == "/root/hello/world");
-
- loc.appendDir("/to");
-
- REQUIRE(loc.directory() == "/root/hello/to");
- REQUIRE(loc.filename() == "world");
- REQUIRE(loc.fullPath() == "/root/hello/to/world");
-}
-
TEST_CASE("unknown target location", M) {
Package pack(Package::UnknownType, "a");
@@ -94,15 +70,26 @@ TEST_CASE("script target location", M) {
Package pack(Package::ScriptType, "file.name");
pack.setCategory(&cat);
- const InstallLocation loc = pack.targetLocation();
- REQUIRE(loc ==
- InstallLocation("/Scripts/ReaScripts/Category Name", "file.name"));
+ const Path path = pack.targetLocation();
+
+ Path expected;
+ expected.append("Scripts");
+ expected.append("ReaScripts");
+ expected.append("Category Name");
+ expected.append("file.name");
+
+ REQUIRE(path == expected);
}
TEST_CASE("script target location without category", M) {
Package pack(Package::ScriptType, "file.name");
- const InstallLocation loc = pack.targetLocation();
- REQUIRE(loc ==
- InstallLocation("/Scripts/ReaScripts", "file.name"));
+ const Path path = pack.targetLocation();
+
+ Path expected;
+ expected.append("Scripts");
+ expected.append("ReaScripts");
+ expected.append("file.name");
+
+ REQUIRE(path == expected);
}
diff --git a/test/path.cpp b/test/path.cpp
@@ -0,0 +1,77 @@
+#include <catch.hpp>
+
+#include "helper/io.hpp"
+
+#include <path.hpp>
+
+using namespace std;
+
+static const char *M = "[path]";
+
+TEST_CASE("compare paths", M) {
+ Path a;
+ a.append("hello");
+ a.append("world");
+
+ Path b;
+ b.append("chunky");
+ b.append("bacon");
+
+ REQUIRE_FALSE(a == b);
+ REQUIRE(a != b);
+
+ REQUIRE(a == a);
+ REQUIRE_FALSE(a != a);
+}
+
+TEST_CASE("prepend and append path components", M) {
+ Path path;
+ REQUIRE(path.empty());
+ REQUIRE(path.size() == 0);
+ REQUIRE(path.join() == string());
+ REQUIRE(path.dirname() == string());
+ REQUIRE(path.basename() == string());
+
+ path.prepend("world");
+ REQUIRE_FALSE(path.empty());
+ REQUIRE(path.size() == 1);
+ REQUIRE(path.join() == "world");
+ REQUIRE(path.dirname() == string());
+ REQUIRE(path.basename() == "world");
+
+ path.prepend("hello");
+ REQUIRE(path.size() == 2);
+#ifndef _WIN32
+ REQUIRE(path.join() == "hello/world");
+#else
+ REQUIRE(path.join() == "hello\\world");
+#endif
+ REQUIRE(path.dirname() == "hello");
+ REQUIRE(path.basename() == "world");
+
+ path.append("test");
+ REQUIRE(path.size() == 3);
+#ifndef _WIN32
+ REQUIRE(path.join() == "hello/world/test");
+ REQUIRE(path.dirname() == "hello/world");
+#else
+ REQUIRE(path.join() == "hello\\world\\test");
+ REQUIRE(path.dirname() == "hello\\world");
+#endif
+ REQUIRE(path.basename() == "test");
+}
+
+TEST_CASE("concatenate paths", M) {
+ Path a;
+ a.append("hello");
+
+ Path b;
+ b.append("world");
+
+ Path c;
+ c.append("hello");
+ c.append("world");
+
+ REQUIRE(a + b == c);
+ REQUIRE(a + "world" == c);
+}